From 33dc362680082043f377301e050f0f5afc51b79d Mon Sep 17 00:00:00 2001 From: Ryan Shepherd Date: Mon, 11 Sep 2023 17:10:35 -0700 Subject: [PATCH 01/16] Use temporary local impl of winmd lib --- natvis/cppwinrtvisualizer.vcxproj | 6 +- natvis/packages.config | 1 - natvis/winmd/impl/base.h | 71 ++ natvis/winmd/impl/winmd_reader/cache.h | 257 +++++++ natvis/winmd/impl/winmd_reader/column.h | 551 ++++++++++++++ .../impl/winmd_reader/custom_attribute.h | 385 ++++++++++ natvis/winmd/impl/winmd_reader/database.h | 624 ++++++++++++++++ natvis/winmd/impl/winmd_reader/enum.h | 329 +++++++++ natvis/winmd/impl/winmd_reader/enum_traits.h | 170 +++++ natvis/winmd/impl/winmd_reader/filter.h | 136 ++++ natvis/winmd/impl/winmd_reader/flags.h | 660 +++++++++++++++++ natvis/winmd/impl/winmd_reader/helpers.h | 129 ++++ natvis/winmd/impl/winmd_reader/index.h | 56 ++ natvis/winmd/impl/winmd_reader/key.h | 213 ++++++ natvis/winmd/impl/winmd_reader/pe.h | 163 ++++ natvis/winmd/impl/winmd_reader/schema.h | 694 ++++++++++++++++++ natvis/winmd/impl/winmd_reader/signature.h | 589 +++++++++++++++ natvis/winmd/impl/winmd_reader/table.h | 420 +++++++++++ natvis/winmd/impl/winmd_reader/type_helpers.h | 43 ++ natvis/winmd/impl/winmd_reader/view.h | 308 ++++++++ natvis/winmd/winmd_reader.h | 20 + 21 files changed, 5820 insertions(+), 5 deletions(-) create mode 100644 natvis/winmd/impl/base.h create mode 100644 natvis/winmd/impl/winmd_reader/cache.h create mode 100644 natvis/winmd/impl/winmd_reader/column.h create mode 100644 natvis/winmd/impl/winmd_reader/custom_attribute.h create mode 100644 natvis/winmd/impl/winmd_reader/database.h create mode 100644 natvis/winmd/impl/winmd_reader/enum.h create mode 100644 natvis/winmd/impl/winmd_reader/enum_traits.h create mode 100644 natvis/winmd/impl/winmd_reader/filter.h create mode 100644 natvis/winmd/impl/winmd_reader/flags.h create mode 100644 natvis/winmd/impl/winmd_reader/helpers.h create mode 100644 natvis/winmd/impl/winmd_reader/index.h create mode 100644 natvis/winmd/impl/winmd_reader/key.h create mode 100644 natvis/winmd/impl/winmd_reader/pe.h create mode 100644 natvis/winmd/impl/winmd_reader/schema.h create mode 100644 natvis/winmd/impl/winmd_reader/signature.h create mode 100644 natvis/winmd/impl/winmd_reader/table.h create mode 100644 natvis/winmd/impl/winmd_reader/type_helpers.h create mode 100644 natvis/winmd/impl/winmd_reader/view.h create mode 100644 natvis/winmd/winmd_reader.h diff --git a/natvis/cppwinrtvisualizer.vcxproj b/natvis/cppwinrtvisualizer.vcxproj index 3cf92f1cd..a244fa2ed 100644 --- a/natvis/cppwinrtvisualizer.vcxproj +++ b/natvis/cppwinrtvisualizer.vcxproj @@ -1,6 +1,5 @@ - Debug @@ -131,7 +130,7 @@ Disabled false VSDEBUGENG_USE_CPP11_SCOPED_ENUMS;_DEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - $(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories) + .\winmd;$(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories) stdcpp20 pch.h @@ -211,7 +210,7 @@ true false VSDEBUGENG_USE_CPP11_SCOPED_ENUMS;NDEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - $(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories) + .\winmd;$(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories) stdcpp20 pch.h Guard @@ -321,6 +320,5 @@ - \ No newline at end of file diff --git a/natvis/packages.config b/natvis/packages.config index 356e82f7f..9de65ed35 100644 --- a/natvis/packages.config +++ b/natvis/packages.config @@ -2,5 +2,4 @@ - \ No newline at end of file diff --git a/natvis/winmd/impl/base.h b/natvis/winmd/impl/base.h new file mode 100644 index 000000000..60e54e23e --- /dev/null +++ b/natvis/winmd/impl/base.h @@ -0,0 +1,71 @@ +#pragma once + +#if defined(_WIN32) +#include +#include +#else +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_DEBUG) +#define XLANG_DEBUG +#define XLANG_ASSERT assert +#else +#define XLANG_ASSERT(expression) ((void)0) +#endif + +namespace winmd +{ + using namespace std::literals; + + namespace impl + { + [[noreturn]] inline void throw_invalid(std::string const& message) + { + throw std::invalid_argument(message); + } + + template + [[noreturn]] inline void throw_invalid(std::string message, T const&... args) + { + (message.append(args), ...); + throw std::invalid_argument(message); + } + + template + auto c_str(std::basic_string_view const& view) noexcept + { + if (*(view.data() + view.size())) + { + std::terminate(); + } + + return view.data(); + } + + inline bool starts_with(std::string_view const& value, std::string_view const& match) noexcept + { + return 0 == value.compare(0, match.size(), match); + } + } +} diff --git a/natvis/winmd/impl/winmd_reader/cache.h b/natvis/winmd/impl/winmd_reader/cache.h new file mode 100644 index 000000000..6d113e5a6 --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/cache.h @@ -0,0 +1,257 @@ + +namespace winmd::reader +{ + struct cache + { + cache() = default; + cache(cache const&) = delete; + cache& operator=(cache const&) = delete; + + template + explicit cache(C const& files, TypeFilter filter) + { + for (auto&& file : files) + { + auto& db = m_databases.emplace_back(file, this); + + for (auto&& type : db.TypeDef) + { + if (type.Flags().value == 0 || is_nested(type) || !filter(type)) + { + continue; + } + + 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) + { + for (auto&&[name, type] : members.types) + { + add_type_to_members(type, members); + } + } + } + + template + explicit cache(C const& files) : cache{ files, default_type_filter{} } + { + } + + template + explicit cache(std::string const& file, TypeFilter filter) : cache{ std::vector{ file }, filter } + { + } + + explicit cache(std::string const& file) : cache{ std::vector{ file }, default_type_filter{} } + { + } + + struct default_type_filter + { + bool operator()(TypeDef const&) const noexcept + { + return true; + } + }; + + TypeDef find(std::string_view const& type_namespace, std::string_view const& type_name) const noexcept + { + auto ns = m_namespaces.find(type_namespace); + + if (ns == m_namespaces.end()) + { + return {}; + } + + auto type = ns->second.types.find(type_name); + + if (type == ns->second.types.end()) + { + return {}; + } + + return type->second; + } + + TypeDef find(std::string_view const& type_string) const + { + auto pos = type_string.rfind('.'); + + if (pos == std::string_view::npos) + { + impl::throw_invalid("Type '", type_string, "' is missing a namespace qualifier"); + } + + return find(type_string.substr(0, pos), type_string.substr(pos + 1, type_string.size())); + } + + TypeDef find_required(std::string_view const& type_namespace, std::string_view const& type_name) const + { + auto definition = find(type_namespace, type_name); + + if (!definition) + { + impl::throw_invalid("Type '", type_namespace, ".", type_name, "' could not be found"); + } + + return definition; + } + + TypeDef find_required(std::string_view const& type_string) const + { + auto pos = type_string.rfind('.'); + + if (pos == std::string_view::npos) + { + impl::throw_invalid("Type '", type_string, "' is missing a namespace qualifier"); + } + + return find_required(type_string.substr(0, pos), type_string.substr(pos + 1, type_string.size())); + } + + auto const& databases() const noexcept + { + return m_databases; + } + + auto const& namespaces() const noexcept + { + return m_namespaces; + } + + void remove_type(std::string_view const& ns, std::string_view const& name) + { + auto m = m_namespaces.find(ns); + if (m == m_namespaces.end()) + { + return; + } + auto& members = m->second; + + auto remove = [&](auto&& collection, auto&& name) + { + auto pos = std::find_if(collection.begin(), collection.end(), [&](auto&& type) + { + return type.TypeName() == name; + }); + + if (pos != collection.end()) + { + collection.erase(pos); + } + }; + + remove(members.interfaces, name); + remove(members.classes, name); + remove(members.enums, name); + remove(members.structs, name); + remove(members.delegates, name); + } + + // This won't invalidate any existing database or row_base (e.g. TypeDef) instances + // However, it may invalidate iterators and references to namespace_members, because those are stored in std::vector + template + void add_database(std::string_view const& file, TypeFilter filter) + { + auto& db = m_databases.emplace_back(file, this); + for (auto&& type : db.TypeDef) + { + if (type.Flags().value == 0 || is_nested(type) || !filter(type)) + { + continue; + } + + auto& ns = m_namespaces[type.TypeNamespace()]; + auto[iter, inserted] = ns.types.try_emplace(type.TypeName(), type); + if (inserted) + { + add_type_to_members(type, ns); + } + } + + for (auto&& row : db.NestedClass) + { + m_nested_types[row.EnclosingType()].push_back(row.NestedType()); + } + } + + void add_database(std::string_view const& file) + { + add_database(file, default_type_filter{}); + } + + 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; + std::vector interfaces; + std::vector classes; + std::vector enums; + std::vector structs; + std::vector delegates; + std::vector attributes; + std::vector contracts; + }; + + using namespace_type = std::pair const&; + + private: + + void add_type_to_members(TypeDef const& type, namespace_members& members) + { + switch (get_category(type)) + { + case category::interface_type: + members.interfaces.push_back(type); + return; + case category::class_type: + if (extends_type(type, "System"sv, "Attribute"sv)) + { + members.attributes.push_back(type); + return; + } + members.classes.push_back(type); + return; + case category::enum_type: + members.enums.push_back(type); + return; + case category::struct_type: + if (get_attribute(type, "Windows.Foundation.Metadata"sv, "ApiContractAttribute"sv)) + { + members.contracts.push_back(type); + return; + } + members.structs.push_back(type); + return; + case category::delegate_type: + members.delegates.push_back(type); + return; + } + } + + std::list m_databases; + std::map m_namespaces; + std::map> m_nested_types; + }; +} \ No newline at end of file diff --git a/natvis/winmd/impl/winmd_reader/column.h b/natvis/winmd/impl/winmd_reader/column.h new file mode 100644 index 000000000..4ab754aac --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/column.h @@ -0,0 +1,551 @@ + +namespace winmd::reader +{ + template + template + auto row_base::get_list(uint32_t const column) const + { + auto const& my_table = get_database().template get_table(); + auto const& target_table = get_database().template get_table(); + + auto first = target_table.begin() + get_value(column) - 1; + auto last = target_table.end(); + if (index() + 1 < my_table.size()) + { + last = target_table.begin() + my_table[index() + 1].template get_value(column) - 1; + } + return std::pair{ first, last }; + } + + template + template + auto row_base::get_target_row(uint32_t const column) const + { + return get_database().template get_table()[get_value(column) - 1]; + } + + template + template + auto row_base::get_parent_row() const + { + struct compare + { + bool operator()(T const& lhs, uint32_t rhs) const noexcept + { + return lhs.template get_value(ParentColumn) < rhs; + } + bool operator()(uint32_t lhs, T const& rhs) const noexcept + { + return lhs < rhs.template get_value(ParentColumn); + } + }; + auto const& map = get_database().template get_table(); + return std::upper_bound(map.begin(), map.end(), index() + 1, compare{}) - 1; + } + + inline auto TypeDef::GenericParam() const + { + return equal_range(get_database().GenericParam, coded_index()); + } + + inline auto MethodDef::GenericParam() const + { + return equal_range(get_database().GenericParam, coded_index()); + } + + inline auto TypeDef::InterfaceImpl() const + { + struct compare + { + bool operator()(uint32_t const left, reader::InterfaceImpl const& right) noexcept + { + return left < right.get_value(0); + } + + bool operator()(reader::InterfaceImpl const& left, uint32_t const right) noexcept + { + return left.get_value(0) < right; + } + }; + + return equal_range(get_database().InterfaceImpl, index() + 1, compare{}); + } + + inline auto TypeDef::FieldList() const + { + return get_list(4); + } + + inline auto TypeDef::MethodList() const + { + return get_list(5); + } + + inline auto MethodDef::ParamList() const + { + return get_list(5); + } + + inline auto MethodDef::Parent() const + { + return get_parent_row(); + } + + inline auto Field::Parent() const + { + return get_parent_row(); + } + + inline auto InterfaceImpl::Class() const + { + return get_target_row(0); + } + + inline auto MethodSemantics::Method() const + { + return get_target_row(1); + } + + inline auto PropertyMap::Parent() const + { + return get_target_row(0); + } + + inline auto Property::MethodSemantic() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto Property::Parent() const + { + return get_parent_row().Parent(); + } + + inline auto PropertyMap::PropertyList() const + { + return get_list(1); + } + + inline auto EventMap::Parent() const + { + return get_target_row(0); + } + + inline auto EventMap::EventList() const + { + return get_list(1); + } + + inline auto Event::MethodSemantic() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto Event::Parent() const + { + return get_parent_row().Parent(); + } + + inline auto TypeDef::PropertyList() const + { + auto const& map = get_database().get_table(); + auto index = this->index() + 1; + auto iter = std::find_if(map.begin(), map.end(), [index](PropertyMap const& elem) + { + return elem.get_value(0) == index; + }); + if (iter == map.end()) + { + auto const& props = get_database().get_table(); + return std::pair{ props.end(), props.end() }; + } + else + { + return iter.PropertyList(); + } + } + + inline auto TypeDef::EventList() const + { + auto const& map = get_database().get_table(); + auto index = this->index() + 1; + auto iter = std::find_if(map.begin(), map.end(), [index](EventMap const& elem) + { + return elem.get_value(0) == index; + }); + if (iter == map.end()) + { + auto const& props = get_database().get_table(); + return std::pair{ props.end(), props.end() }; + } + else + { + return iter.EventList(); + } + } + + inline auto TypeDef::MethodImplList() const + { + struct compare + { + bool operator()(MethodImpl const& lhs, uint32_t rhs) const noexcept + { + return lhs.get_value(1) < rhs; + } + bool operator()(uint32_t lhs, MethodImpl const& rhs) const noexcept + { + return lhs < rhs.get_value(1); + } + }; + return equal_range(get_database().get_table(), index() + 1, compare{}); + } + + inline auto Field::Constant() const + { + auto const range = equal_range(get_database().Constant, coded_index()); + reader::Constant result; + if (range.second != range.first) + { + XLANG_ASSERT(range.second - range.first == 1); + result = range.first; + } + return result; + } + + inline auto Param::Constant() const + { + auto const range = equal_range(get_database().Constant, coded_index()); + reader::Constant result; + if (range.second != range.first) + { + XLANG_ASSERT(range.second - range.first == 1); + result = range.first; + } + return result; + } + + inline auto Property::Constant() const + { + auto const range = equal_range(get_database().Constant, coded_index()); + reader::Constant result; + if (range.second != range.first) + { + XLANG_ASSERT(range.second - range.first == 1); + result = range.first; + } + return result; + } + + inline auto MethodImpl::Class() const + { + return get_target_row(0); + } + + inline auto Constant::ValueBoolean() const + { + XLANG_ASSERT(Type() == ConstantType::Boolean); + return get_blob(2).as(); + } + + inline auto Constant::ValueChar() const + { + XLANG_ASSERT(Type() == ConstantType::Char); + return get_blob(2).as(); + } + + inline auto Constant::ValueInt8() const + { + XLANG_ASSERT(Type() == ConstantType::Int8); + return get_blob(2).as(); + } + + inline auto Constant::ValueUInt8() const + { + XLANG_ASSERT(Type() == ConstantType::UInt8); + return get_blob(2).as(); + } + + inline auto Constant::ValueInt16() const + { + XLANG_ASSERT(Type() == ConstantType::Int16); + return get_blob(2).as(); + } + + inline auto Constant::ValueUInt16() const + { + XLANG_ASSERT(Type() == ConstantType::UInt16); + return get_blob(2).as(); + } + + inline auto Constant::ValueInt32() const + { + XLANG_ASSERT(Type() == ConstantType::Int32); + return get_blob(2).as(); + } + + inline auto Constant::ValueUInt32() const + { + XLANG_ASSERT(Type() == ConstantType::UInt32); + return get_blob(2).as(); + } + + inline auto Constant::ValueInt64() const + { + XLANG_ASSERT(Type() == ConstantType::Int64); + return get_blob(2).as(); + } + + inline auto Constant::ValueUInt64() const + { + XLANG_ASSERT(Type() == ConstantType::UInt64); + return get_blob(2).as(); + } + + inline auto Constant::ValueFloat32() const + { + XLANG_ASSERT(Type() == ConstantType::Float32); + return get_blob(2).as(); + } + + inline auto Constant::ValueFloat64() const + { + XLANG_ASSERT(Type() == ConstantType::Float64); + return get_blob(2).as(); + } + + inline auto Constant::ValueString() const + { + XLANG_ASSERT(Type() == ConstantType::String); + return get_blob(2).as_u16string_constant(); + } + + inline auto Constant::ValueClass() const + { + XLANG_ASSERT(Type() == ConstantType::Class); + XLANG_ASSERT(get_blob(2).as() == 0); + return nullptr; + } + + inline Constant::constant_type Constant::Value() const + { + switch (Type()) + { + case ConstantType::Boolean: + return ValueBoolean(); + case ConstantType::Char: + return ValueChar(); + case ConstantType::Int8: + return ValueInt8(); + case ConstantType::UInt8: + return ValueUInt8(); + case ConstantType::Int16: + return ValueInt16(); + case ConstantType::UInt16: + return ValueUInt16(); + case ConstantType::Int32: + return ValueInt32(); + case ConstantType::UInt32: + return ValueUInt32(); + case ConstantType::Int64: + return ValueInt64(); + case ConstantType::UInt64: + return ValueUInt64(); + case ConstantType::Float32: + return ValueFloat32(); + case ConstantType::Float64: + return ValueFloat64(); + case ConstantType::String: + return ValueString(); + case ConstantType::Class: + return ValueClass(); + default: + impl::throw_invalid("Invalid constant type"); + } + } + + inline auto MethodDef::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto Field::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto TypeRef::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto TypeDef::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto Param::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto InterfaceImpl::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto MemberRef::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto Module::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto Property::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto Event::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto StandAloneSig::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto ModuleRef::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto TypeSpec::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto Assembly::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto AssemblyRef::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto File::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto ExportedType::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto ManifestResource::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto GenericParam::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto GenericParamConstraint::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + inline auto MethodSpec::CustomAttribute() const + { + return equal_range(get_database().get_table(), coded_index()); + } + + struct AssemblyVersion + { + uint16_t MajorVersion; + uint16_t MinorVersion; + uint16_t BuildNumber; + uint16_t RevisionNumber; + }; + + inline auto Assembly::Version() const + { + auto const temp = get_value(1); + return AssemblyVersion{ static_cast(temp & 0xffff), static_cast((temp >> 16) & 0xffff), static_cast((temp >> 32) & 0xffff), static_cast((temp >> 48) & 0xffff) }; + } + + inline auto AssemblyRef::Version() const + { + auto const temp = get_value(0); + return AssemblyVersion{ static_cast(temp & 0xffff), static_cast((temp >> 16) & 0xffff), static_cast((temp >> 32) & 0xffff), static_cast((temp >> 48) & 0xffff) }; + } + + inline auto AssemblyRefOS::AssemblyRef() const + { + return get_target_row(3); + } + + inline auto AssemblyRefProcessor::AssemblyRef() const + { + return get_target_row(3); + } + + inline auto ClassLayout::Parent() const + { + 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/natvis/winmd/impl/winmd_reader/custom_attribute.h b/natvis/winmd/impl/winmd_reader/custom_attribute.h new file mode 100644 index 000000000..36b56a949 --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/custom_attribute.h @@ -0,0 +1,385 @@ + +namespace winmd::reader +{ + inline auto CustomAttribute::TypeNamespaceAndName() const + { + if (Type().type() == CustomAttributeType::MemberRef) + { + auto const& member_parent = Type().MemberRef().Class(); + switch (member_parent.type()) + { + case MemberRefParent::TypeDef: + { + auto const& def = member_parent.TypeDef(); + return std::pair{ def.TypeNamespace(), def.TypeName() }; + } + + case MemberRefParent::TypeRef: + { + auto const& ref = member_parent.TypeRef(); + return std::pair{ ref.TypeNamespace(), ref.TypeName() }; + } + default: + impl::throw_invalid("A CustomAttribute MemberRef should only be a TypeDef or TypeRef"); + } + } + else + { + auto const& def = Type().MethodDef().Parent(); + return std::pair{ def.TypeNamespace(), def.TypeName() }; + } + } + + struct ElemSig + { + struct SystemType + { + std::string_view name; + }; + + struct EnumValue + { + EnumDefinition type; + using value_type = std::variant; + value_type value; + + bool equals_enumerator(std::string_view const& name) const + { + auto field = type.get_enumerator(name); + auto constant_value = std::visit([](auto&& v) { return Constant::constant_type{ v }; }, value); + return field.Constant().Value() == constant_value; + } + }; + + using value_type = std::variant; + + ElemSig(database const& db, ParamSig const& param, byte_view& data) + : value{ read_element(db, param, data) } + { + } + + ElemSig(SystemType type) + : value(type) + { + } + + ElemSig(EnumDefinition const& enum_def, byte_view& data) + : value{ EnumValue{enum_def, read_enum(enum_def.m_underlying_type, data) } } + { + } + + ElemSig(ElementType type, byte_view& data) + : value{ read_primitive(type, data) } + { + } + + static value_type read_element(database const& db, ParamSig const& param, byte_view& data) + { + auto const& type = param.Type().Type(); + if (auto element_type = std::get_if(&type)) + { + return read_primitive(*element_type, data); + } + else if (auto type_index = std::get_if>(&type)) + { + if ((type_index->type() == TypeDefOrRef::TypeRef && type_index->TypeRef().TypeNamespace() == "System" && type_index->TypeRef().TypeName() == "Type") || + (type_index->type() == TypeDefOrRef::TypeDef && type_index->TypeDef().TypeNamespace() == "System" && type_index->TypeDef().TypeName() == "Type")) + { + return SystemType{ read(data) }; + } + else + { + // Should be an enum. Resolve it. + auto resolve_type = [&db, &type_index]() -> TypeDef + { + if (type_index->type() == TypeDefOrRef::TypeDef) + { + return type_index->TypeDef(); + } + auto const& typeref = type_index->TypeRef(); + return db.get_cache().find_required(typeref.TypeNamespace(), typeref.TypeName()); + }; + TypeDef const& enum_type = resolve_type(); + if (!enum_type.is_enum()) + { + impl::throw_invalid("CustomAttribute params that are TypeDefOrRef must be an enum or System.Type"); + } + + auto const& enum_def = enum_type.get_enum_definition(); + return EnumValue{ enum_def, read_enum(enum_def.m_underlying_type, data) }; + } + } + impl::throw_invalid("Custom attribute params must be primitives, enums, or System.Type"); + } + + static value_type read_primitive(ElementType type, byte_view& data) + { + switch (type) + { + case ElementType::Boolean: + return read(data); + + case ElementType::Char: + return read(data); + + case ElementType::I1: + return read(data); + + case ElementType::U1: + return read(data); + + case ElementType::I2: + return read(data); + + case ElementType::U2: + return read(data); + + case ElementType::I4: + return read(data); + + case ElementType::U4: + return read(data); + + case ElementType::I8: + return read(data); + + case ElementType::U8: + return read(data); + + case ElementType::R4: + return read(data); + + case ElementType::R8: + return read(data); + + case ElementType::String: + return read(data); + + default: + impl::throw_invalid("Non-primitive type encountered"); + } + } + + static EnumValue::value_type read_enum(ElementType type, byte_view& data) + { + switch (type) + { + case ElementType::Boolean: + return read(data); + + case ElementType::Char: + return read(data); + + case ElementType::I1: + return read(data); + + case ElementType::U1: + return read(data); + + case ElementType::I2: + return read(data); + + case ElementType::U2: + return read(data); + + case ElementType::I4: + return read(data); + + case ElementType::U4: + return read(data); + + case ElementType::I8: + return read(data); + + case ElementType::U8: + return read(data); + + default: + impl::throw_invalid("Invalid underling enum type encountered"); + } + } + + value_type value; + }; + + struct FixedArgSig + { + using value_type = std::variant>; + + FixedArgSig(database const& db, ParamSig const& ctor_param, byte_view& data) + : value{ read_arg(db, ctor_param, data) } + {} + + FixedArgSig(ElemSig::SystemType type) + : value{ ElemSig{type} } + {} + + FixedArgSig(EnumDefinition const& enum_def, byte_view& data) + : value{ ElemSig{ enum_def, data } } + {} + + FixedArgSig(ElementType type, bool is_array, byte_view& data) + : value{ read_arg(type, is_array, data) } + {} + + static value_type read_arg(database const& db, ParamSig const& ctor_param, byte_view& data) + { + auto const& type_sig = ctor_param.Type(); + if (type_sig.is_szarray()) + { + std::vector elems; + auto const num_elements = read(data); + if (num_elements != 0xffffffff) + { + if (num_elements > data.size()) + { + impl::throw_invalid("Invalid blob array size"); + } + elems.reserve(num_elements); + for (uint32_t i = 0; i < num_elements; ++i) + { + elems.emplace_back(db, ctor_param, data); + } + } + return elems; + } + else + { + return ElemSig{ db, ctor_param, data }; + } + } + + static value_type read_arg(ElementType type, bool is_array, byte_view& data) + { + if (is_array) + { + std::vector elems; + auto const num_elements = read(data); + if (num_elements != 0xffffffff) + { + if (num_elements > data.size()) + { + impl::throw_invalid("Invalid blob array size"); + } + elems.reserve(num_elements); + for (uint32_t i = 0; i < num_elements; ++i) + { + elems.emplace_back(type, data); + } + } + return elems; + } + else + { + return ElemSig{ type, data }; + } + } + + value_type value; + }; + + struct NamedArgSig + { + NamedArgSig(database const& db, byte_view& data) + : value{ parse_value(db, data) } + {} + + std::string_view name; + FixedArgSig value; + + private: + FixedArgSig parse_value(database const& db, byte_view& data) + { + auto const field_or_prop = read(data); + if (field_or_prop != ElementType::Field && field_or_prop != ElementType::Property) + { + impl::throw_invalid("NamedArg must be either FIELD or PROPERTY"); + } + + auto type = read(data); + switch (type) + { + case ElementType::Type: + name = read(data); + return FixedArgSig{ ElemSig::SystemType{read(data)} }; + + case ElementType::Enum: + { + auto type_string = read(data); + name = read(data); + auto type_def = db.get_cache().find(type_string); + if (!type_def) + { + impl::throw_invalid("CustomAttribute named param referenced unresolved enum type"); + } + if (!type_def.is_enum()) + { + impl::throw_invalid("CustomAttribute named param referenced non-enum type"); + } + + return FixedArgSig{ type_def.get_enum_definition(), data }; + } + + default: + { + bool const is_array = (type == ElementType::SZArray); + if (is_array) + { + type = read(data); + } + if (type < ElementType::Boolean || ElementType::String < type) + { + impl::throw_invalid("CustomAttribute named param must be a primitive, System.Type, or an Enum"); + } + name = read(data); + return FixedArgSig{ type, is_array, data }; + } + } + } + }; + + struct CustomAttributeSig + { + CustomAttributeSig(table_base const* table, byte_view& data, MethodDefSig const& ctor) + { + database const& db = table->get_database(); + auto const prolog = read(data); + if (prolog != 0x0001) + { + impl::throw_invalid("CustomAttribute blobs must start with prolog of 0x0001"); + } + + for (auto const& param : ctor.Params()) + { + m_fixed_args.push_back(FixedArgSig{ db, param, data }); + } + + const auto num_named_args = read(data); + if (num_named_args > data.size()) + { + impl::throw_invalid("Invalid blob array size"); + } + m_named_args.reserve(num_named_args); + + for (uint16_t i = 0; i < num_named_args; ++i) + { + m_named_args.emplace_back(db, data); + } + } + + std::vector const& FixedArgs() const noexcept { return m_fixed_args; } + std::vector const& NamedArgs() const noexcept { return m_named_args; } + + private: + std::vector m_fixed_args; + std::vector m_named_args; + }; + + inline auto CustomAttribute::Value() const + { + auto const ctor = Type(); + MethodDefSig const& method_sig = ctor.type() == CustomAttributeType::MemberRef ? ctor.MemberRef().MethodSignature() : ctor.MethodDef().Signature(); + auto cursor = get_blob(2); + return CustomAttributeSig{ get_table(), cursor, method_sig }; + } +} diff --git a/natvis/winmd/impl/winmd_reader/database.h b/natvis/winmd/impl/winmd_reader/database.h new file mode 100644 index 000000000..c65de70f5 --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/database.h @@ -0,0 +1,624 @@ + +namespace winmd::impl +{ + constexpr uint8_t bits_needed(uint32_t value) noexcept + { + --value; + uint8_t bits{ 1 }; + + while (value >>= 1) + { + ++bits; + } + + return bits; + } + + static_assert(bits_needed(2) == 1); + static_assert(bits_needed(3) == 2); + static_assert(bits_needed(4) == 2); + static_assert(bits_needed(5) == 3); + static_assert(bits_needed(22) == 5); +} + +namespace winmd::reader +{ + struct cache; + + struct database + { + database(database&&) = delete; + database& operator=(database&&) = delete; + + static bool is_database(std::string_view const& path) + { + file_view file{ path }; + + if (file.size() < sizeof(impl::image_dos_header)) + { + return false; + } + + auto dos = file.as(); + + if (dos.e_signature != 0x5A4D) // IMAGE_DOS_SIGNATURE + { + return false; + } + + if (file.size() < (dos.e_lfanew + sizeof(impl::image_nt_headers32))) + { + return false; + } + + auto pe = file.as(dos.e_lfanew); + + if (pe.FileHeader.NumberOfSections == 0 || pe.FileHeader.NumberOfSections > 100) + { + return false; + } + + impl::image_section_header const* sections{}; + uint32_t com_virtual_address{}; + if (pe.OptionalHeader.Magic == 0x10B) // PE32 + { + com_virtual_address = pe.OptionalHeader.DataDirectory[14].VirtualAddress; // IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR + sections = &file.as(dos.e_lfanew + sizeof(impl::image_nt_headers32)); + } + else if (pe.OptionalHeader.Magic == 0x20B) // PE32+ + { + auto pe_plus = file.as(dos.e_lfanew); + com_virtual_address = pe_plus.OptionalHeader.DataDirectory[14].VirtualAddress; // IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR + sections = &file.as(dos.e_lfanew + sizeof(impl::image_nt_headers32plus)); + } + else + { + impl::throw_invalid("Invalid optional header magic value"); + } + auto sections_end = sections + pe.FileHeader.NumberOfSections; + auto section = section_from_rva(sections, sections_end, com_virtual_address); + + if (section == sections_end) + { + return false; + } + + auto offset = offset_from_rva(*section, com_virtual_address); + + auto cli = file.as(offset); + + if (cli.cb != sizeof(impl::image_cor20_header)) + { + return false; + } + + section = section_from_rva(sections, sections_end, cli.MetaData.VirtualAddress); + + if (section == sections_end) + { + return false; + } + + offset = offset_from_rva(*section, cli.MetaData.VirtualAddress); + + if (file.as(offset) != 0x424a5342) + { + return false; + } + + return true; + } + + explicit database(std::vector&& buffer, cache const* cache = nullptr) : m_buffer{ std::move(buffer) }, m_view{ m_buffer.data(), m_buffer.data() + m_buffer.size() }, m_cache{ cache } + { + initialize(); + } + + explicit database(std::string_view const& path, cache const* cache = nullptr) : m_view{ path }, m_path{ path }, m_cache{ cache } + { + initialize(); + } + + table TypeRef{ this }; + table GenericParamConstraint{ this }; + table TypeSpec{ this }; + table TypeDef{ this }; + table CustomAttribute{ this }; + table MethodDef{ this }; + table MemberRef{ this }; + table Module{ this }; + table Param{ this }; + table InterfaceImpl{ this }; + table Constant{ this }; + table Field{ this }; + table FieldMarshal{ this }; + table DeclSecurity{ this }; + table ClassLayout{ this }; + table FieldLayout{ this }; + table StandAloneSig{ this }; + table EventMap{ this }; + table Event{ this }; + table PropertyMap{ this }; + table Property{ this }; + table MethodSemantics{ this }; + table MethodImpl{ this }; + table ModuleRef{ this }; + table ImplMap{ this }; + table FieldRVA{ this }; + table Assembly{ this }; + table AssemblyProcessor{ this }; + table AssemblyOS{ this }; + table AssemblyRef{ this }; + table AssemblyRefProcessor{ this }; + table AssemblyRefOS{ this }; + table File{ this }; + table ExportedType{ this }; + table ManifestResource{ this }; + table NestedClass{ this }; + table GenericParam{ this }; + table MethodSpec{ this }; + + template + table const& get_table() const noexcept; + + cache const& get_cache() const noexcept + { + return *m_cache; + } + + std::string const& path() const noexcept + { + return m_path; + } + + std::string_view get_string(uint32_t const index) const + { + auto view = m_strings.seek(index); + auto last = std::find(view.begin(), view.end(), 0); + + if (last == view.end()) + { + impl::throw_invalid("Missing string terminator"); + } + + return { reinterpret_cast(view.begin()), static_cast(last - view.begin()) }; + } + + byte_view get_blob(uint32_t const index) const + { + auto view = m_blobs.seek(index); + auto initial_byte = view.as(); + uint32_t blob_size_bytes{}; + + switch (initial_byte >> 5) + { + case 0: + case 1: + case 2: + case 3: + blob_size_bytes = 1; + initial_byte &= 0x7f; + break; + + case 4: + case 5: + blob_size_bytes = 2; + initial_byte &= 0x3f; + break; + + case 6: + blob_size_bytes = 4; + initial_byte &= 0x1f; + break; + + default: + impl::throw_invalid("Invalid blob encoding"); + } + + uint32_t blob_size{ initial_byte }; + + for (auto&& byte : view.sub(1, blob_size_bytes - 1)) + { + blob_size = (blob_size << 8) + byte; + } + + return { view.sub(blob_size_bytes, blob_size) }; + } + + private: + void initialize() + { + auto dos = m_view.as(); + + if (dos.e_signature != 0x5A4D) // IMAGE_DOS_SIGNATURE + { + impl::throw_invalid("Invalid DOS signature"); + } + + auto pe = m_view.as(dos.e_lfanew); + + if (pe.FileHeader.NumberOfSections == 0 || pe.FileHeader.NumberOfSections > 100) + { + impl::throw_invalid("Invalid PE section count"); + } + + impl::image_section_header const* sections{}; + uint32_t com_virtual_address{}; + if (pe.OptionalHeader.Magic == 0x10B) // PE32 + { + com_virtual_address = pe.OptionalHeader.DataDirectory[14].VirtualAddress; // IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR + sections = &m_view.as(dos.e_lfanew + sizeof(impl::image_nt_headers32)); + } + else if (pe.OptionalHeader.Magic == 0x20B) // PE32+ + { + auto pe_plus = m_view.as(dos.e_lfanew); + com_virtual_address = pe_plus.OptionalHeader.DataDirectory[14].VirtualAddress; // IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR + sections = &m_view.as(dos.e_lfanew + sizeof(impl::image_nt_headers32plus)); + } + else + { + impl::throw_invalid("Invalid optional header magic value"); + } + auto sections_end = sections + pe.FileHeader.NumberOfSections; + auto section = section_from_rva(sections, sections_end, com_virtual_address); + + if (section == sections_end) + { + impl::throw_invalid("PE section containing CLI header not found"); + } + + auto offset = offset_from_rva(*section, com_virtual_address); + + auto cli = m_view.as(offset); + + if (cli.cb != sizeof(impl::image_cor20_header)) + { + impl::throw_invalid("Invalid CLI header"); + } + + section = section_from_rva(sections, sections_end, cli.MetaData.VirtualAddress); + + if (section == sections_end) + { + impl::throw_invalid("PE section containing CLI metadata not found"); + } + + offset = offset_from_rva(*section, cli.MetaData.VirtualAddress); + + if (m_view.as(offset) != 0x424a5342) + { + impl::throw_invalid("CLI metadata magic signature not found"); + } + + auto version_length = m_view.as(offset + 12); + auto stream_count = m_view.as(offset + version_length + 18); + auto view = m_view.seek(offset + version_length + 20); + byte_view tables; + + for (uint16_t i{}; i < stream_count; ++i) + { + auto stream = view.as(); + auto name = view.as>(8); + + if (name.data() == "#Strings"sv) + { + m_strings = m_view.sub(offset + stream.offset, stream.size); + } + else if (name.data() == "#Blob"sv) + { + m_blobs = m_view.sub(offset + stream.offset, stream.size); + } + else if (name.data() == "#GUID"sv) + { + m_guids = m_view.sub(offset + stream.offset, stream.size); + } + else if (name.data() == "#~"sv) + { + tables = m_view.sub(offset + stream.offset, stream.size); + } + else if (name.data() != "#US"sv) + { + impl::throw_invalid("Unknown metadata stream"); + } + + view = view.seek(stream_offset(name.data())); + } + + std::bitset<8> const heap_sizes{ tables.as(6) }; + uint8_t const string_index_size = heap_sizes.test(0) ? 4 : 2; + uint8_t const guid_index_size = heap_sizes.test(1) ? 4 : 2; + uint8_t const blob_index_size = heap_sizes.test(2) ? 4 : 2; + + std::bitset<64> const valid_bits{ tables.as(8) }; + view = tables.seek(24); + + for (uint32_t i{}; i < 64; ++i) + { + if (!valid_bits.test(i)) + { + continue; + } + + auto row_count = view.as(); + view = view.seek(4); + + switch (i) + { + case 0x00: Module.set_row_count(row_count); break; + case 0x01: TypeRef.set_row_count(row_count); break; + case 0x02: TypeDef.set_row_count(row_count); break; + case 0x04: Field.set_row_count(row_count); break; + case 0x06: MethodDef.set_row_count(row_count); break; + case 0x08: Param.set_row_count(row_count); break; + case 0x09: InterfaceImpl.set_row_count(row_count); break; + case 0x0a: MemberRef.set_row_count(row_count); break; + case 0x0b: Constant.set_row_count(row_count); break; + case 0x0c: CustomAttribute.set_row_count(row_count); break; + case 0x0d: FieldMarshal.set_row_count(row_count); break; + case 0x0e: DeclSecurity.set_row_count(row_count); break; + case 0x0f: ClassLayout.set_row_count(row_count); break; + case 0x10: FieldLayout.set_row_count(row_count); break; + case 0x11: StandAloneSig.set_row_count(row_count); break; + case 0x12: EventMap.set_row_count(row_count); break; + case 0x14: Event.set_row_count(row_count); break; + case 0x15: PropertyMap.set_row_count(row_count); break; + case 0x17: Property.set_row_count(row_count); break; + case 0x18: MethodSemantics.set_row_count(row_count); break; + case 0x19: MethodImpl.set_row_count(row_count); break; + case 0x1a: ModuleRef.set_row_count(row_count); break; + case 0x1b: TypeSpec.set_row_count(row_count); break; + case 0x1c: ImplMap.set_row_count(row_count); break; + case 0x1d: FieldRVA.set_row_count(row_count); break; + case 0x20: Assembly.set_row_count(row_count); break; + case 0x21: AssemblyProcessor.set_row_count(row_count); break; + case 0x22: AssemblyOS.set_row_count(row_count); break; + case 0x23: AssemblyRef.set_row_count(row_count); break; + case 0x24: AssemblyRefProcessor.set_row_count(row_count); break; + case 0x25: AssemblyRefOS.set_row_count(row_count); break; + case 0x26: File.set_row_count(row_count); break; + case 0x27: ExportedType.set_row_count(row_count); break; + case 0x28: ManifestResource.set_row_count(row_count); break; + case 0x29: NestedClass.set_row_count(row_count); break; + case 0x2a: GenericParam.set_row_count(row_count); break; + case 0x2b: MethodSpec.set_row_count(row_count); break; + case 0x2c: GenericParamConstraint.set_row_count(row_count); break; + default: impl::throw_invalid("Unknown metadata table"); + }; + } + + table_base const empty_table{ nullptr }; + + auto const TypeDefOrRef = composite_index_size(TypeDef, TypeRef, TypeSpec); + auto const HasConstant = composite_index_size(Field, Param, Property); + auto const HasCustomAttribute = composite_index_size(MethodDef, Field, TypeRef, TypeDef, Param, InterfaceImpl, MemberRef, Module, Property, Event, StandAloneSig, ModuleRef, TypeSpec, Assembly, AssemblyRef, File, ExportedType, ManifestResource, GenericParam, GenericParamConstraint, MethodSpec); + auto const HasFieldMarshal = composite_index_size(Field, Param); + auto const HasDeclSecurity = composite_index_size(TypeDef, MethodDef, Assembly); + auto const MemberRefParent = composite_index_size(TypeDef, TypeRef, ModuleRef, MethodDef, TypeSpec); + auto const HasSemantics = composite_index_size(Event, Property); + auto const MethodDefOrRef = composite_index_size(MethodDef, MemberRef); + auto const MemberForwarded = composite_index_size(Field, MethodDef); + auto const Implementation = composite_index_size(File, AssemblyRef, ExportedType); + auto const CustomAttributeType = composite_index_size(MethodDef, MemberRef, empty_table, empty_table, empty_table); + auto const ResolutionScope = composite_index_size(Module, ModuleRef, AssemblyRef, TypeRef); + auto const TypeOrMethodDef = composite_index_size(TypeDef, MethodDef); + + Assembly.set_columns(4, 8, 4, blob_index_size, string_index_size, string_index_size); + AssemblyOS.set_columns(4, 4, 4); + AssemblyProcessor.set_columns(4); + AssemblyRef.set_columns(8, 4, blob_index_size, string_index_size, string_index_size, blob_index_size); + AssemblyRefOS.set_columns(4, 4, 4, AssemblyRef.index_size()); + AssemblyRefProcessor.set_columns(4, AssemblyRef.index_size()); + ClassLayout.set_columns(2, 4, TypeDef.index_size()); + Constant.set_columns(2, HasConstant, blob_index_size); + CustomAttribute.set_columns(HasCustomAttribute, CustomAttributeType, blob_index_size); + DeclSecurity.set_columns(2, HasDeclSecurity, blob_index_size); + EventMap.set_columns(TypeDef.index_size(), Event.index_size()); + Event.set_columns(2, string_index_size, TypeDefOrRef); + ExportedType.set_columns(4, 4, string_index_size, string_index_size, Implementation); + Field.set_columns(2, string_index_size, blob_index_size); + FieldLayout.set_columns(4, Field.index_size()); + FieldMarshal.set_columns(HasFieldMarshal, blob_index_size); + FieldRVA.set_columns(4, Field.index_size()); + File.set_columns(4, string_index_size, blob_index_size); + GenericParam.set_columns(2, 2, TypeOrMethodDef, string_index_size); + GenericParamConstraint.set_columns(GenericParam.index_size(), TypeDefOrRef); + ImplMap.set_columns(2, MemberForwarded, string_index_size, ModuleRef.index_size()); + InterfaceImpl.set_columns(TypeDef.index_size(), TypeDefOrRef); + ManifestResource.set_columns(4, 4, string_index_size, Implementation); + MemberRef.set_columns(MemberRefParent, string_index_size, blob_index_size); + MethodDef.set_columns(4, 2, 2, string_index_size, blob_index_size, Param.index_size()); + MethodImpl.set_columns(TypeDef.index_size(), MethodDefOrRef, MethodDefOrRef); + MethodSemantics.set_columns(2, MethodDef.index_size(), HasSemantics); + MethodSpec.set_columns(MethodDefOrRef, blob_index_size); + Module.set_columns(2, string_index_size, guid_index_size, guid_index_size, guid_index_size); + ModuleRef.set_columns(string_index_size); + NestedClass.set_columns(TypeDef.index_size(), TypeDef.index_size()); + Param.set_columns(2, 2, string_index_size); + Property.set_columns(2, string_index_size, blob_index_size); + PropertyMap.set_columns(TypeDef.index_size(), Property.index_size()); + StandAloneSig.set_columns(blob_index_size); + TypeDef.set_columns(4, string_index_size, string_index_size, TypeDefOrRef, Field.index_size(), MethodDef.index_size()); + TypeRef.set_columns(ResolutionScope, string_index_size, string_index_size); + TypeSpec.set_columns(blob_index_size); + + Module.set_data(view); + TypeRef.set_data(view); + TypeDef.set_data(view); + Field.set_data(view); + MethodDef.set_data(view); + Param.set_data(view); + InterfaceImpl.set_data(view); + MemberRef.set_data(view); + Constant.set_data(view); + CustomAttribute.set_data(view); + FieldMarshal.set_data(view); + DeclSecurity.set_data(view); + ClassLayout.set_data(view); + FieldLayout.set_data(view); + StandAloneSig.set_data(view); + EventMap.set_data(view); + Event.set_data(view); + PropertyMap.set_data(view); + Property.set_data(view); + MethodSemantics.set_data(view); + MethodImpl.set_data(view); + ModuleRef.set_data(view); + TypeSpec.set_data(view); + ImplMap.set_data(view); + FieldRVA.set_data(view); + Assembly.set_data(view); + AssemblyProcessor.set_data(view); + AssemblyOS.set_data(view); + AssemblyRef.set_data(view); + AssemblyRefProcessor.set_data(view); + AssemblyRefOS.set_data(view); + File.set_data(view); + ExportedType.set_data(view); + ManifestResource.set_data(view); + NestedClass.set_data(view); + GenericParam.set_data(view); + MethodSpec.set_data(view); + GenericParamConstraint.set_data(view); + } + + struct stream_range + { + uint32_t offset; + uint32_t size; + }; + + static bool composite_index_size(uint32_t const row_count, uint8_t const bits) + { + return row_count < (1ull << (16 - bits)); + } + + template + static uint8_t composite_index_size(Tables const&... tables) + { + return (composite_index_size(tables.size(), impl::bits_needed(sizeof...(tables))) && ...) ? 2 : 4; + } + + static uint32_t stream_offset(std::string_view const& name) noexcept + { + uint32_t padding = 4 - name.size() % 4; + + if (padding == 0) + { + padding = 4; + } + + return static_cast(8 + name.size() + padding); + } + + static impl::image_section_header const* section_from_rva(impl::image_section_header const* const first, impl::image_section_header const* const last, uint32_t const rva) noexcept + { + return std::find_if(first, last, [rva](auto&& section) noexcept + { + return rva >= section.VirtualAddress && rva < section.VirtualAddress + section.Misc.VirtualSize; + }); + } + + static uint32_t offset_from_rva(impl::image_section_header const& section, uint32_t const rva) noexcept + { + return rva - section.VirtualAddress + section.PointerToRawData; + } + + std::vector m_buffer; + file_view m_view; + + std::string const m_path; + byte_view m_strings; + byte_view m_blobs; + byte_view m_guids; + cache const* m_cache; + }; + + template + inline byte_view row_base::get_blob(uint32_t const column) const + { + return get_database().get_blob(m_table->get_value(m_index, column)); + } + + template + inline std::string_view row_base::get_string(uint32_t const column) const + { + return get_database().get_string(m_table->get_value(m_index, column)); + } + + template <> + inline table const& database::get_table() const noexcept { return Module; } + template <> + inline table const& database::get_table() const noexcept { return TypeRef; } + template <> + inline table const& database::get_table() const noexcept { return TypeDef; } + template <> + inline table const& database::get_table() const noexcept { return Field; } + template <> + inline table const& database::get_table() const noexcept { return MethodDef; } + template <> + inline table const& database::get_table() const noexcept { return Param; } + template <> + inline table const& database::get_table() const noexcept { return InterfaceImpl; } + template <> + inline table const& database::get_table() const noexcept { return MemberRef; } + template <> + inline table const& database::get_table() const noexcept { return Constant; } + template <> + inline table const& database::get_table() const noexcept { return CustomAttribute; } + template <> + inline table const& database::get_table() const noexcept { return FieldMarshal; } + template <> + inline table const& database::get_table() const noexcept { return DeclSecurity; } + template <> + inline table const& database::get_table() const noexcept { return ClassLayout; } + template <> + inline table const& database::get_table() const noexcept { return FieldLayout; } + template <> + inline table const& database::get_table() const noexcept { return StandAloneSig; } + template <> + inline table const& database::get_table() const noexcept { return EventMap; } + template <> + inline table const& database::get_table() const noexcept { return Event; } + template <> + inline table const& database::get_table() const noexcept { return PropertyMap; } + template <> + inline table const& database::get_table() const noexcept { return Property; } + template <> + inline table const& database::get_table() const noexcept { return MethodSemantics; } + template <> + inline table const& database::get_table() const noexcept { return MethodImpl; } + template <> + inline table const& database::get_table() const noexcept { return ModuleRef; } + template <> + inline table const& database::get_table() const noexcept { return TypeSpec; } + template <> + inline table const& database::get_table() const noexcept { return ImplMap; } + template <> + inline table const& database::get_table() const noexcept { return FieldRVA; } + template <> + inline table const& database::get_table() const noexcept { return Assembly; } + template <> + inline table const& database::get_table() const noexcept { return AssemblyProcessor; } + template <> + inline table const& database::get_table() const noexcept { return AssemblyOS; } + template <> + inline table const& database::get_table() const noexcept { return AssemblyRef; } + template <> + inline table const& database::get_table() const noexcept { return AssemblyRefProcessor; } + template <> + inline table const& database::get_table() const noexcept { return AssemblyRefOS; } + template <> + inline table const& database::get_table() const noexcept { return File; } + template <> + inline table const& database::get_table() const noexcept { return ExportedType; } + template <> + inline table const& database::get_table() const noexcept { return ManifestResource; } + template <> + inline table const& database::get_table() const noexcept { return NestedClass; } + template <> + inline table const& database::get_table() const noexcept { return GenericParam; } + template <> + inline table const& database::get_table() const noexcept { return MethodSpec; } + template <> + inline table const& database::get_table() const noexcept { return GenericParamConstraint; } +} diff --git a/natvis/winmd/impl/winmd_reader/enum.h b/natvis/winmd/impl/winmd_reader/enum.h new file mode 100644 index 000000000..8792e3e78 --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/enum.h @@ -0,0 +1,329 @@ + +namespace winmd::reader +{ + template + struct coded_index_bits : std::integral_constant {}; + + template + inline constexpr uint32_t coded_index_bits_v = coded_index_bits::value; + + enum class TypeDefOrRef : uint32_t + { + TypeDef, + TypeRef, + TypeSpec, + }; + template <> + struct coded_index_bits : std::integral_constant {}; + + enum class HasConstant : uint32_t + { + Field, + Param, + Property, + }; + template <> + struct coded_index_bits : std::integral_constant {}; + + enum class HasCustomAttribute : uint32_t + { + MethodDef, + Field, + TypeRef, + TypeDef, + Param, + InterfaceImpl, + MemberRef, + Module, + Permission, + Property, + Event, + StandAloneSig, + ModuleRef, + TypeSpec, + Assembly, + AssemblyRef, + File, + ExportedType, + ManifestResource, + GenericParam, + GenericParamConstraint, + MethodSpec, + }; + template <> + struct coded_index_bits : std::integral_constant {}; + + enum class HasFieldMarshal : uint32_t + { + Field, + Param, + }; + template <> + struct coded_index_bits : std::integral_constant {}; + + enum class HasDeclSecurity : uint32_t + { + TypeDef, + MethodDef, + Assembly, + }; + template <> + struct coded_index_bits : std::integral_constant {}; + + enum class MemberRefParent : uint32_t + { + TypeDef, + TypeRef, + ModuleRef, + MethodDef, + TypeSpec, + }; + template <> + struct coded_index_bits : std::integral_constant {}; + + enum class HasSemantics : uint32_t + { + Event, + Property, + }; + template <> + struct coded_index_bits : std::integral_constant {}; + + enum class MethodDefOrRef : uint32_t + { + MethodDef, + MemberRef, + }; + template <> + struct coded_index_bits : std::integral_constant {}; + + enum class MemberForwarded : uint32_t + { + Field, + MethodDef, + }; + template <> + struct coded_index_bits : std::integral_constant {}; + + enum class Implementation : uint32_t + { + File, + AssemblyRef, + ExportedType, + }; + template <> + struct coded_index_bits : std::integral_constant {}; + + enum class CustomAttributeType : uint32_t + { + MethodDef = 2, + MemberRef, + }; + template <> + struct coded_index_bits : std::integral_constant {}; + + enum class ResolutionScope : uint32_t + { + Module, + ModuleRef, + AssemblyRef, + TypeRef, + }; + template <> + struct coded_index_bits : std::integral_constant {}; + + enum class TypeOrMethodDef : uint32_t + { + TypeDef, + MethodDef, + }; + template <> + struct coded_index_bits : std::integral_constant {}; + + enum class MemberAccess : uint16_t + { + CompilerControlled = 0x0000, // Member not referenceable + Private = 0x0001, + FamAndAssem = 0x0002, // Accessible by subtypes only in this Assembly + Assembly = 0x0003, // Accessible by anyone in this Assembly + Family = 0x0004, // aka Protected + FamOrAssem = 0x0005, // Accessible by subtypes anywhere, plus anyone in this Assembly + Public = 0x0006, + }; + + enum class TypeVisibility : uint32_t + { + NotPublic = 0x00000000, + Public = 0x00000001, + NestedPublic = 0x00000002, + NestedPrivate = 0x00000003, + NestedFamily = 0x00000004, + NestedAssembly = 0x00000005, + NestedFamANDAssem = 0x00000006, + NestedFamORAssem = 0x00000007, + }; + + enum class TypeLayout : uint32_t + { + AutoLayout = 0x00000000, + SequentialLayout = 0x00000008, + ExplicitLayout = 0x00000010, + }; + + enum class TypeSemantics : uint32_t + { + Class = 0x00000000, + Interface = 0x00000020, + }; + + enum class StringFormat : uint32_t + { + AnsiClass = 0x00000000, + UnicodeClass = 0x00010000, + AutoClass = 0x00020000, + CustomFormatClass = 0x00030000, + CustomFormatMask = 0x00C00000, + }; + + enum class CodeType : uint16_t + { + IL = 0x0000, // Method impl is CIL + Native = 0x0001, // Method impl is native + OPTIL = 0x0002, // Reserved: shall be zero in conforming implementations + Runtime = 0x0003, // Method impl is provided by the runtime + }; + + enum class Managed : uint16_t + { + Unmanaged = 0x0004, + Managed = 0x0000, + }; + + enum class VtableLayout : uint16_t + { + ReuseSlot = 0x0000, // Method reuses existing slot in a vtable + NewSlot = 0x0100, // Method always gets a new slot in the vtable + }; + + enum class GenericParamVariance : uint16_t + { + None = 0x0000, + Covariant = 0x0001, + Contravariant = 0x0002 + }; + + enum class GenericParamSpecialConstraint : uint16_t + { + ReferenceTypeConstraint = 0x0004, + NotNullableValueTypeConstraint = 0x0008, + DefaultConstructorConstraint = 0x0010 + }; + + enum class ConstantType : uint16_t + { + Boolean = 0x02, + Char = 0x03, + Int8 = 0x04, + UInt8 = 0x05, + Int16 = 0x06, + UInt16 = 0x07, + Int32 = 0x08, + UInt32 = 0x09, + Int64 = 0x0a, + UInt64 = 0x0b, + Float32 = 0x0c, + Float64 = 0x0d, + String = 0x0e, + Class = 0x12 + }; + + enum class ElementType : uint8_t + { + End = 0x00, // Sentinel value + + Void = 0x01, + Boolean = 0x02, + Char = 0x03, + I1 = 0x04, + U1 = 0x05, + I2 = 0x06, + U2 = 0x07, + I4 = 0x08, + U4 = 0x09, + I8 = 0x0a, + U8 = 0x0b, + R4 = 0x0c, + R8 = 0x0d, + String = 0x0e, + + Ptr = 0x0f, // Followed by TypeSig + ByRef = 0x10, // Followed by TypeSig + ValueType = 0x11, // Followed by TypeDef or TypeRef + Class = 0x12, // Followed by TypeDef or TypeRef + Var = 0x13, // Generic parameter in a type definition, represented as unsigned integer + Array = 0x14, + GenericInst = 0x15, + TypedByRef = 0x16, + + I = 0x18, // System.IntPtr + U = 0x19, // System.UIntPtr + + FnPtr = 0x1b, // Followed by full method signature + Object = 0x1c, // System.Object + SZArray = 0x1d, + MVar = 0x1e, // Generic parameter in a method definition, represented as unsigned integer + CModReqd = 0x1f, // Required modifier, followed by a TypeDef or TypeRef + CModOpt = 0x20, // Optional modifier, followed by a TypeDef or TypeRef + Internal = 0x21, + + Modifier = 0x40, // Or'd with folowing element types + Sentinel = 0x41, // Sentinel for vararg method signature + + Pinned = 0x45, + + Type = 0x50, // System.Type + TaggedObject = 0x51, // Boxed object (in custom attributes) + Field = 0x53, // Custom attribute field + Property = 0x54, // Custom attribute property + Enum = 0x55, // Custom attribute enum + }; + + enum class CallingConvention : uint8_t + { + Default = 0x00, + VarArg = 0x05, + Field = 0x06, + LocalSig = 0x07, + Property = 0x08, + GenericInst = 0x10, + Mask = 0x0f, + + HasThis = 0x20, + ExplicitThis = 0x40, + Generic = 0x10, + }; + + enum class AssemblyHashAlgorithm : uint32_t + { + None = 0x0000, + Reserved_MD5 = 0x8003, + SHA1 = 0x8004, + }; + + enum class AssemblyFlags : uint32_t + { + PublicKey = 0x0001, // The assembly reference holds the full (unhashed) public key + Retargetable = 0x0100, + WindowsRuntime = 0x0200, + DisableJITcompileOptimizer = 0x4000, + EnableJITcompileTracking = 0x8000, + }; + + template + constexpr inline T enum_mask(T value, T mask) noexcept + { + static_assert(std::is_enum_v); + using val = std::underlying_type_t; + return static_cast(static_cast(value) & static_cast(mask)); + } +} diff --git a/natvis/winmd/impl/winmd_reader/enum_traits.h b/natvis/winmd/impl/winmd_reader/enum_traits.h new file mode 100644 index 000000000..7d33d40e3 --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/enum_traits.h @@ -0,0 +1,170 @@ + +namespace winmd::reader +{ + struct Module; + struct TypeRef; + struct TypeDef; + struct Field; + struct MethodDef; + struct Param; + struct InterfaceImpl; + struct MemberRef; + struct Constant; + struct CustomAttribute; + struct FieldMarshal; + struct DeclSecurity; + struct ClassLayout; + struct FieldLayout; + struct StandAloneSig; + struct EventMap; + struct Event; + struct PropertyMap; + struct Property; + struct MethodSemantics; + struct MethodImpl; + struct ModuleRef; + struct TypeSpec; + struct ImplMap; + struct FieldRVA; + struct Assembly; + struct AssemblyProcessor; + struct AssemblyOS; + struct AssemblyRef; + struct AssemblyRefProcessor; + struct AssemblyRefOS; + struct File; + struct ExportedType; + struct ManifestResource; + struct NestedClass; + struct GenericParam; + struct MethodSpec; + struct GenericParamConstraint; + + + template + struct index_tag; + + template + inline constexpr auto index_tag_v = index_tag::value; + + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; + + template<> + struct index_tag : std::integral_constant {}; + template<> + struct index_tag : std::integral_constant {}; +} diff --git a/natvis/winmd/impl/winmd_reader/filter.h b/natvis/winmd/impl/winmd_reader/filter.h new file mode 100644 index 000000000..e682f358f --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/filter.h @@ -0,0 +1,136 @@ + +namespace winmd::reader +{ + struct filter + { + filter() noexcept = default; + + template + filter(T const& includes, T const& excludes) + { + for (auto&& include : includes) + { + m_rules.push_back({ include, true }); + } + + for (auto&& exclude : excludes) + { + m_rules.push_back({ exclude, false }); + } + + std::sort(m_rules.begin(), m_rules.end(), [](auto const& lhs, auto const& rhs) + { + return std::pair{ lhs.first.size(), !lhs.second } > std::pair{ rhs.first.size(), !rhs.second }; + }); + } + + bool includes(TypeDef const& type) const + { + return includes(type.TypeNamespace(), type.TypeName()); + } + + bool includes(std::string_view const& type) const + { + auto position = type.find_last_of('.'); + return includes(type.substr(0, position), type.substr(position + 1)); + } + + bool includes(std::vector const& types) const + { + if (m_rules.empty()) + { + return true; + } + + for (auto&& type : types) + { + if (includes(type.TypeNamespace(), type.TypeName())) + { + return true; + } + } + + return false; + } + + bool includes(cache::namespace_members const& members) const + { + if (m_rules.empty()) + { + return true; + } + + for (auto&& type : members.types) + { + if (includes(type.second.TypeNamespace(), type.second.TypeName())) + { + return true; + } + } + + return false; + } + + template + auto bind_each(std::vector const& types) const + { + return [&](auto& writer) + { + for (auto&& type : types) + { + if (includes(type)) + { + F(writer, type); + } + } + }; + } + + bool empty() const noexcept + { + return m_rules.empty(); + } + + private: + + bool includes(std::string_view const& type_namespace, std::string_view const& type_name) const noexcept + { + if (m_rules.empty()) + { + return true; + } + + for (auto&& rule : m_rules) + { + if (match(type_namespace, type_name, rule.first)) + { + return rule.second; + } + } + + return false; + } + + static bool match(std::string_view const& type_namespace, std::string_view const& type_name, std::string_view const& match) noexcept + { + if (match.size() <= type_namespace.size()) + { + return impl::starts_with(type_namespace, match); + } + + if (!impl::starts_with(match, type_namespace)) + { + return false; + } + + if (match[type_namespace.size()] != '.') + { + return false; + } + + return impl::starts_with(type_name, match.substr(type_namespace.size() + 1)); + } + + std::vector> m_rules; + }; +} diff --git a/natvis/winmd/impl/winmd_reader/flags.h b/natvis/winmd/impl/winmd_reader/flags.h new file mode 100644 index 000000000..62dee7fc4 --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/flags.h @@ -0,0 +1,660 @@ + +namespace winmd::impl +{ + template + struct AttributesBase + { + T value; + protected: + constexpr T get_mask(T mask) const noexcept + { + return value & mask; + } + void set_mask(T arg, T mask) noexcept + { + value = (value & ~mask) | (arg & mask); + } + template + constexpr U get_enum(T mask) const noexcept + { + static_assert(std::is_enum_v); + static_assert(std::is_same_v>); + return static_cast(get_mask(mask)); + } + template + void set_enum(U arg, T mask) noexcept + { + static_assert(std::is_enum_v); + static_assert(std::is_same_v>); + set_mask(static_cast(arg), mask); + } + constexpr bool get_bit(int bit) const noexcept + { + return get_mask(1 << bit); + } + void set_bit(bool arg, int bit) noexcept + { + set_mask(arg << bit, 1 << bit); + } + }; +} + +namespace winmd::reader +{ + struct AssemblyAttributes : impl::AttributesBase + { + constexpr bool WindowsRuntime() const noexcept + { + return get_bit(WindowsRuntime_bit); + } + void WindowsRuntime(bool arg) noexcept + { + set_bit(arg, WindowsRuntime_bit); + } + + private: + static constexpr int WindowsRuntime_bit{ 9 }; + }; + + struct EventAttributes : impl::AttributesBase + { + constexpr bool SpecialName() const noexcept + { + return get_bit(SpecialName_bit); + } + void SpecialName(bool arg) noexcept + { + set_bit(arg, SpecialName_bit); + } + constexpr bool RTSpecialName() const noexcept + { + return get_bit(RTSpecialName_bit); + } + void RTSpecialName(bool arg) noexcept + { + set_bit(arg, RTSpecialName_bit); + } + + private: + static constexpr int SpecialName_bit{ 9 }; + static constexpr int RTSpecialName_bit{ 10 }; + }; + + struct FieldAttributes : impl::AttributesBase + { + constexpr MemberAccess Access() const noexcept + { + return get_enum(Access_mask); + } + void Access(MemberAccess arg) noexcept + { + set_enum(arg, Access_mask); + } + constexpr bool Static() const noexcept + { + return get_bit(Static_bit); + } + constexpr bool InitOnly() const noexcept + { + return get_bit(InitOnly_bit); + } + constexpr bool Literal() const noexcept + { + return get_bit(Literal_bit); + } + constexpr bool NotSerialized() const noexcept + { + return get_bit(NotSerialized_bit); + } + constexpr bool SpecialName() const noexcept + { + return get_bit(SpecialName_bit); + } + constexpr bool PInvokeImpl() const noexcept + { + return get_bit(PInvokeImpl_bit); + } + constexpr bool RTSpecialName() const noexcept + { + return get_bit(RTSpecialName_bit); + } + constexpr bool HasFieldMarshal() const noexcept + { + return get_bit(HasFieldMarshal_bit); + } + constexpr bool HasDefault() const noexcept + { + return get_bit(HasDefault_bit); + } + constexpr bool HasFieldRVA() const noexcept + { + return get_bit(HasFieldRVA_bit); + } + + private: + static constexpr uint16_t Access_mask{ 0x0007 }; + static constexpr int Static_bit{ 4 }; + static constexpr int InitOnly_bit{ 5 }; + static constexpr int Literal_bit{ 6 }; + static constexpr int NotSerialized_bit{ 7 }; + static constexpr int SpecialName_bit{ 9 }; + static constexpr int PInvokeImpl_bit{ 13 }; + static constexpr int RTSpecialName_bit{ 10 }; + static constexpr int HasFieldMarshal_bit{ 12 }; + static constexpr int HasDefault_bit{ 15 }; + static constexpr int HasFieldRVA_bit{ 8 }; + }; + + struct GenericParamAttributes : impl::AttributesBase + { + constexpr GenericParamVariance Variance() const noexcept + { + return get_enum(Variance_mask); + } + void Variance(GenericParamVariance arg) noexcept + { + set_enum(arg, Variance_mask); + } + constexpr GenericParamSpecialConstraint SpecialConstraint() const noexcept + { + return get_enum(SpecialConstraint_mask); + } + void SpecialConstraint(GenericParamSpecialConstraint arg) noexcept + { + set_enum(arg, SpecialConstraint_mask); + } + + private: + static constexpr uint16_t Variance_mask{ 0x0003 }; + static constexpr uint16_t SpecialConstraint_mask{ 0x001c }; + }; + + struct MethodAttributes : impl::AttributesBase + { + constexpr MemberAccess Access() const noexcept + { + return get_enum(Access_mask); + } + void Access(MemberAccess arg) noexcept + { + set_enum(arg, Access_mask); + } + constexpr bool Static() const noexcept + { + return get_bit(Static_bit); + } + void Static(bool arg) noexcept + { + set_bit(arg, Static_bit); + } + constexpr bool Final() const noexcept + { + return get_bit(Final_bit); + } + void Final(bool arg) noexcept + { + set_bit(arg, Final_bit); + } + constexpr bool Virtual() const noexcept + { + return get_bit(Virtual_bit); + } + void Virtual(bool arg) noexcept + { + set_bit(arg, Virtual_bit); + } + constexpr bool HideBySig() const noexcept + { + return get_bit(HideBySig_bit); + } + void HideBySig(bool arg) noexcept + { + set_bit(arg, HideBySig_bit); + } + constexpr VtableLayout Layout() const noexcept + { + return get_enum(VtableLayout_mask); + } + void Layout(VtableLayout arg) noexcept + { + set_enum(arg, VtableLayout_mask); + } + constexpr bool Strict() const noexcept + { + return get_bit(Strict_bit); + } + void Strict(bool arg) noexcept + { + set_bit(arg, Strict_bit); + } + constexpr bool Abstract() const noexcept + { + return get_bit(Abstract_bit); + } + void Abstract(bool arg) noexcept + { + set_bit(arg, Abstract_bit); + } + constexpr bool SpecialName() const noexcept + { + return get_bit(SpecialName_bit); + } + void SpecialName(bool arg) noexcept + { + set_bit(arg, SpecialName_bit); + } + constexpr bool PInvokeImpl() const noexcept + { + return get_bit(PInvokeImpl_bit); + } + void PInvokeImpl(bool arg) noexcept + { + set_bit(arg, PInvokeImpl_bit); + } + constexpr bool UnmanagedExport() const noexcept + { + return get_bit(UnmanagedExport_bit); + } + void UnmanagedExport(bool arg) noexcept + { + set_bit(arg, UnmanagedExport_bit); + } + constexpr bool RTSpecialName() const noexcept + { + return get_bit(RTSpecialName_bit); + } + void RTSpecialName(bool arg) noexcept + { + set_bit(arg, RTSpecialName_bit); + } + constexpr bool HasSecurity() const noexcept + { + return get_bit(HasSecurity_bit); + } + void HasSecurity(bool arg) noexcept + { + set_bit(arg, HasSecurity_bit); + } + constexpr bool RequireSecObject() const noexcept + { + return get_bit(RequireSecObject_bit); + } + void RequireSecObject(bool arg) noexcept + { + set_bit(arg, RequireSecObject_bit); + } + + private: + static constexpr uint16_t Access_mask{ 0x0007 }; + static constexpr int Static_bit{ 4 }; + static constexpr int Final_bit{ 5 }; + static constexpr int Virtual_bit{ 6 }; + static constexpr int HideBySig_bit{ 7 }; + static constexpr uint16_t VtableLayout_mask{ 0x0100 }; + static constexpr int Strict_bit{ 9 }; + static constexpr int Abstract_bit{ 10 }; + static constexpr int SpecialName_bit{ 11 }; + static constexpr int PInvokeImpl_bit{ 13 }; + static constexpr int UnmanagedExport_bit{ 3 }; + static constexpr int RTSpecialName_bit{ 12 }; + static constexpr int HasSecurity_bit{ 14 }; + static constexpr int RequireSecObject_bit{ 15 }; + }; + + struct MethodImplAttributes : impl::AttributesBase + { + constexpr CodeType CodeType() const noexcept + { + return get_enum(CodeType_mask); + } + void CodeType(reader::CodeType arg) noexcept + { + set_enum(arg, CodeType_mask); + } + constexpr Managed Managed() const noexcept + { + return get_enum(Managed_mask); + } + void Managed(reader::Managed arg) noexcept + { + set_enum(arg, Managed_mask); + } + constexpr bool ForwardRef() const noexcept + { + return get_bit(ForwardRef_bit); + } + void ForwardRef(bool arg) noexcept + { + set_bit(arg, ForwardRef_bit); + } + constexpr bool PreserveSig() const noexcept + { + return get_bit(PreserveSig_bit); + } + void PreserveSig(bool arg) noexcept + { + set_bit(arg, PreserveSig_bit); + } + constexpr bool InternalCall() const noexcept + { + return get_bit(InternalCall_bit); + } + void InternalCall(bool arg) noexcept + { + set_bit(arg, InternalCall_bit); + } + constexpr bool Synchronized() const noexcept + { + return get_bit(Synchronized_bit); + } + void Synchronized(bool arg) noexcept + { + set_bit(arg, Synchronized_bit); + } + constexpr bool NoInlining() const noexcept + { + return get_bit(NoInlining_bit); + } + void NoInlining(bool arg) noexcept + { + set_bit(arg, NoInlining_bit); + } + constexpr bool NoOptimization() const noexcept + { + return get_bit(NoOptimization_bit); + } + void NoOptimization(bool arg) noexcept + { + set_bit(arg, NoOptimization_bit); + } + + private: + static constexpr uint16_t CodeType_mask{ 0x0003 }; + static constexpr uint16_t Managed_mask{ 0x0004 }; + static constexpr int ForwardRef_bit{ 4 }; // Method is defined; used primarily in merge scenarios + static constexpr int PreserveSig_bit{ 7 }; // Reserved + static constexpr int InternalCall_bit{ 12 }; // Reserved + static constexpr int Synchronized_bit{ 5 }; // Method is single threaded through the body + static constexpr int NoInlining_bit{ 3 }; // Method cannot be inlined + static constexpr int NoOptimization_bit{ 6 }; // Method will not be optimized when generatinv native code + static constexpr uint16_t MaxMethodImplVal{ 0xffff }; // Range check value + }; + + struct MethodSemanticsAttributes : impl::AttributesBase + { + constexpr bool Setter() const noexcept + { + return get_bit(Setter_bit); + } + void Setter(bool arg) noexcept + { + set_bit(arg, Setter_bit); + } + constexpr bool Getter() const noexcept + { + return get_bit(Getter_bit); + } + void Getter(bool arg) noexcept + { + set_bit(arg, Getter_bit); + } + constexpr bool Other() const noexcept + { + return get_bit(Other_bit); + } + void Other(bool arg) noexcept + { + set_bit(arg, Other_bit); + } + constexpr bool AddOn() const noexcept + { + return get_bit(AddOn_bit); + } + void AddOn(bool arg) noexcept + { + set_bit(arg, AddOn_bit); + } + constexpr bool RemoveOn() const noexcept + { + return get_bit(RemoveOn_bit); + } + void RemoveOn(bool arg) noexcept + { + set_bit(arg, RemoveOn_bit); + } + constexpr bool Fire() const noexcept + { + return get_bit(Fire_bit); + } + void Fire(bool arg) noexcept + { + set_bit(arg, Fire_bit); + } + + private: + static constexpr int Setter_bit{ 0 }; + static constexpr int Getter_bit{ 1 }; + static constexpr int Other_bit{ 2 }; + static constexpr int AddOn_bit{ 3 }; + static constexpr int RemoveOn_bit{ 4 }; + static constexpr int Fire_bit{ 5 }; + }; + + struct ParamAttributes : impl::AttributesBase + { + constexpr bool In() const noexcept + { + return get_bit(In_bit); + } + void In(bool arg) noexcept + { + set_bit(arg, In_bit); + } + constexpr bool Out() const noexcept + { + return get_bit(Out_bit); + } + void Out(bool arg) noexcept + { + set_bit(arg, Out_bit); + } + constexpr bool Optional() const noexcept + { + return get_bit(Optional_bit); + } + void Optional(bool arg) noexcept + { + set_bit(arg, Optional_bit); + } + constexpr bool HasDefault() const noexcept + { + return get_bit(HasDefault_bit); + } + void HasDefault(bool arg) noexcept + { + set_bit(arg, HasDefault_bit); + } + constexpr bool HasFieldMarshal() const noexcept + { + return get_bit(HasFieldMarshal_bit); + } + void HasFieldMarshal(bool arg) noexcept + { + set_bit(arg, HasFieldMarshal_bit); + } + + private: + static constexpr int In_bit{ 0 }; + static constexpr int Out_bit{ 1 }; + static constexpr int Optional_bit{ 4 }; + static constexpr int HasDefault_bit{ 12 }; + static constexpr int HasFieldMarshal_bit{ 13 }; + static constexpr uint16_t Unused_mask{ 0xcfe0 }; + }; + + struct PropertyAttributes : impl::AttributesBase + { + constexpr bool SpecialName() const noexcept + { + return get_bit(SpecialName_bit); + } + void SpecialName(bool arg) noexcept + { + set_bit(arg, SpecialName_bit); + } + constexpr bool RTSpecialName() const noexcept + { + return get_bit(RTSpecialName_bit); + } + void RTSpecialName(bool arg) noexcept + { + set_bit(arg, RTSpecialName_bit); + } + constexpr bool HasDefault() const noexcept + { + return get_bit(HasDefault_bit); + } + void HasDefault(bool arg) noexcept + { + set_bit(arg, HasDefault_bit); + } + + private: + static constexpr int SpecialName_bit{ 9 }; + static constexpr int RTSpecialName_bit{ 10 }; + static constexpr int HasDefault_bit{ 12 }; + static constexpr uint16_t Unused_mask{ 0xe9ff }; + }; + + struct TypeAttributes : impl::AttributesBase + { + constexpr TypeVisibility Visibility() const noexcept + { + return get_enum(Visibility_mask); + } + void Visibility(TypeVisibility arg) noexcept + { + set_enum(arg, Visibility_mask); + } + constexpr TypeLayout Layout() const noexcept + { + return get_enum(Layout_mask); + } + void Layout(TypeLayout arg) noexcept + { + set_enum(arg, Layout_mask); + } + constexpr TypeSemantics Semantics() const noexcept + { + return get_enum(Semantics_mask); + } + void Semantics(TypeSemantics arg) noexcept + { + set_enum(arg, Semantics_mask); + } + constexpr bool Abstract() const noexcept + { + return get_bit(Abstract_bit); + } + void Abstract(bool arg) noexcept + { + set_bit(arg, Abstract_bit); + } + constexpr bool Sealed() const noexcept + { + return get_bit(Sealed_bit); + } + void Sealed(bool arg) noexcept + { + set_bit(arg, Sealed_bit); + } + constexpr bool SpecialName() const noexcept + { + return get_bit(SpecialName_bit); + } + void SpecialName(bool arg) noexcept + { + set_bit(arg, SpecialName_bit); + } + constexpr bool Import() const noexcept + { + return get_bit(Import_bit); + } + void Import(bool arg) noexcept + { + set_bit(arg, Import_bit); + } + constexpr bool Serializable() const noexcept + { + return get_bit(Serializable_bit); + } + void Serializable(bool arg) noexcept + { + set_bit(arg, Serializable_bit); + } + constexpr bool WindowsRuntime() const noexcept + { + return get_bit(WindowsRuntime_bit); + } + void WindowsRuntime(bool arg) noexcept + { + set_bit(arg, WindowsRuntime_bit); + } + constexpr StringFormat StringFormat() const noexcept + { + return get_enum(StringFormat_mask); + } + void StringFormat(reader::StringFormat arg) noexcept + { + set_enum(arg, StringFormat_mask); + } + constexpr bool BeforeFieldInit() const noexcept + { + return get_bit(BeforeFieldInit_bit); + } + void BeforeFieldInit(bool arg) noexcept + { + set_bit(arg, BeforeFieldInit_bit); + } + constexpr bool RTSpecialName() const noexcept + { + return get_bit(RTSpecialName_bit); + } + void RTSpecialName(bool arg) noexcept + { + set_bit(arg, RTSpecialName_bit); + } + constexpr bool HasSecurity() const noexcept + { + return get_bit(HasSecurity_bit); + } + void HasSecurity(bool arg) noexcept + { + set_bit(arg, HasSecurity_bit); + } + constexpr bool IsTypeForwarder() const noexcept + { + return get_bit(IsTypeForwarder_bit); + } + void IsTypeForwarder(bool arg) noexcept + { + set_bit(arg, IsTypeForwarder_bit); + } + + private: + static constexpr uint32_t Visibility_mask{ 0x00000007 }; + static constexpr uint32_t Layout_mask{ 0x00000018 }; + static constexpr uint32_t Semantics_mask{ 0x00000020 }; + static constexpr int Abstract_bit{ 7 }; + static constexpr int Sealed_bit{ 8 }; + static constexpr int SpecialName_bit{ 10 }; + static constexpr int Import_bit{ 12 }; + static constexpr int Serializable_bit{ 13 }; + static constexpr int WindowsRuntime_bit{ 14 }; + static constexpr uint32_t StringFormat_mask{ 0x00030000 }; + static constexpr int BeforeFieldInit_bit{ 20 }; + static constexpr int RTSpecialName_bit{ 11 }; + static constexpr int HasSecurity_bit{ 18 }; + static constexpr int IsTypeForwarder_bit{ 21 }; + }; +} diff --git a/natvis/winmd/impl/winmd_reader/helpers.h b/natvis/winmd/impl/winmd_reader/helpers.h new file mode 100644 index 000000000..75ecc3b8b --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/helpers.h @@ -0,0 +1,129 @@ + +namespace winmd::reader +{ + template + bool empty(std::pair const& range) noexcept + { + return range.first == range.second; + } + + template + std::size_t size(std::pair const& range) noexcept + { + return range.second - range.first; + } + + inline auto find(TypeRef const& type) + { + 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) + { + 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) + { + if (type.type() == TypeDefOrRef::TypeRef) + { + return find_required(type.TypeRef()); + } + else if (type.type() == TypeDefOrRef::TypeDef) + { + return type.TypeDef(); + } + else + { + XLANG_ASSERT(false); + return {}; + } + } + + inline bool is_const(ParamSig const& param) + { + auto is_type_const = [](auto&& type) + { + return type.TypeNamespace() == "System.Runtime.CompilerServices" && type.TypeName() == "IsConst"; + }; + + for (auto const& cmod : param.CustomMod()) + { + auto type = cmod.Type(); + + if (type.type() == TypeDefOrRef::TypeDef) + { + if (is_type_const(type.TypeDef())) + { + return true; + } + } + else if (type.type() == TypeDefOrRef::TypeRef) + { + if (is_type_const(type.TypeRef())) + { + return true; + } + } + } + + return false; + }; +} diff --git a/natvis/winmd/impl/winmd_reader/index.h b/natvis/winmd/impl/winmd_reader/index.h new file mode 100644 index 000000000..6a7fc2263 --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/index.h @@ -0,0 +1,56 @@ + +namespace winmd::reader +{ + template <> struct typed_index : index_base + { + using index_base::index_base; + + auto MemberRef() const; + auto MethodDef() const; + }; + + template <> struct typed_index : index_base + { + using index_base::index_base; + + auto Field() const; + auto Param() const; + auto Property() const; + }; + + template <> struct typed_index : index_base + { + using index_base::index_base; + + auto Property() const; + auto Event() const; + }; + + template <> struct typed_index : index_base + { + using index_base::index_base; + + auto MethodDef() const; + auto MemberRef() const; + }; + + template <> struct typed_index : index_base + { + using index_base::index_base; + + auto Module() const; + auto ModuleRef() const; + auto AssemblyRef() const; + auto TypeRef() const; + }; + + template <> struct typed_index : index_base + { + using index_base::index_base; + + TypeDef TypeDef() const; + TypeRef TypeRef() const; + TypeSpec TypeSpec() const; + auto CustomAttribute() const; + }; +} diff --git a/natvis/winmd/impl/winmd_reader/key.h b/natvis/winmd/impl/winmd_reader/key.h new file mode 100644 index 000000000..f26aefaac --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/key.h @@ -0,0 +1,213 @@ + +namespace winmd::reader +{ + template + template + Row index_base::get_row() const + { + XLANG_ASSERT(type() == (index_tag_v)); + return get_database().template get_table()[index()]; + } + + inline auto typed_index::MemberRef() const + { + return get_row(); + } + + inline auto typed_index::MethodDef() const + { + return get_row(); + } + + inline auto typed_index::Field() const + { + return get_row(); + } + + inline auto typed_index::Param() const + { + return get_row(); + } + + inline auto typed_index::Property() const + { + return get_row(); + } + + inline auto typed_index::Property() const + { + return get_row(); + } + + inline auto typed_index::Event() const + { + return get_row(); + } + + inline auto typed_index::MethodDef() const + { + return get_row(); + } + + inline auto typed_index::MemberRef() const + { + return get_row(); + } + + inline auto typed_index::Module() const + { + return get_row(); + } + + inline auto typed_index::ModuleRef() const + { + return get_row(); + } + + inline auto typed_index::AssemblyRef() const + { + return get_row(); + } + + inline auto typed_index::TypeRef() const + { + return get_row(); + } + + inline TypeDef typed_index::TypeDef() const + { + return get_row(); + } + + inline TypeRef typed_index::TypeRef() const + { + return get_row(); + } + + inline TypeSpec typed_index::TypeSpec() const + { + return get_row(); + } + + inline auto typed_index::CustomAttribute() const + { + if (type() == TypeDefOrRef::TypeDef) + { + return TypeDef().CustomAttribute(); + } + + if (type() == TypeDefOrRef::TypeRef) + { + return TypeRef().CustomAttribute(); + } + + return TypeSpec().CustomAttribute(); + } + + inline auto typed_index::TypeRef() const + { + return get_row(); + } + + inline auto typed_index::TypeDef() const + { + return get_row(); + } + + inline bool TypeDef::is_enum() const + { + return extends_type(*this, "System"sv, "Enum"sv); + } + + struct EnumDefinition + { + explicit EnumDefinition(TypeDef const& type) + : m_typedef(type) + { + XLANG_ASSERT(type.is_enum()); + for (auto field : type.FieldList()) + { + if (!field.Flags().Literal() && !field.Flags().Static()) + { + XLANG_ASSERT(m_underlying_type == ElementType::End); + m_underlying_type = std::get(field.Signature().Type().Type()); + XLANG_ASSERT(ElementType::Boolean <= m_underlying_type && m_underlying_type <= ElementType::U8); + } + } + } + + auto get_enumerator(std::string_view const& name) const + { + auto fields = m_typedef.FieldList(); + + auto field = std::find_if(begin(fields), end(fields), [&](auto&& field) + { + return field.Name() == name; + }); + + XLANG_ASSERT(field != end(fields)); + return field; + } + + TypeDef m_typedef; + ElementType m_underlying_type{}; + + }; + + inline auto TypeDef::get_enum_definition() const + { + return EnumDefinition{ *this }; + } + + template + CustomAttribute get_attribute(T const& row, std::string_view const& type_namespace, std::string_view const& type_name) + { + for (auto&& attribute : row.CustomAttribute()) + { + auto pair = attribute.TypeNamespaceAndName(); + + if (pair.first == type_namespace && pair.second == type_name) + { + return attribute; + } + } + + 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/natvis/winmd/impl/winmd_reader/pe.h b/natvis/winmd/impl/winmd_reader/pe.h new file mode 100644 index 000000000..ce25498de --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/pe.h @@ -0,0 +1,163 @@ + +namespace winmd::impl +{ + struct image_dos_header + { + uint16_t e_signature; + uint16_t e_cblp; + uint16_t e_cp; + uint16_t e_crlc; + uint16_t e_cparhdr; + uint16_t e_minalloc; + uint16_t e_maxalloc; + uint16_t e_ss; + uint16_t e_sp; + uint16_t e_csum; + uint16_t e_ip; + uint16_t e_cs; + uint16_t e_lfarlc; + uint16_t e_ovno; + uint16_t e_res[4]; + uint16_t e_oemid; + uint16_t e_oeminfo; + uint16_t e_res2[10]; + int32_t e_lfanew; + }; + + struct image_file_header + { + uint16_t Machine; + uint16_t NumberOfSections; + uint32_t TimeDateStamp; + uint32_t PointerToSymbolTable; + uint32_t NumberOfSymbols; + uint16_t SizeOfOptionalHeader; + uint16_t Characteristics; + }; + + struct image_data_directory + { + uint32_t VirtualAddress; + uint32_t Size; + }; + + struct image_optional_header32 + { + uint16_t Magic; + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; + uint32_t BaseOfCode; + uint32_t BaseOfData; + uint32_t ImageBase; + uint32_t SectionAlignment; + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint32_t SizeOfStackReserve; + uint32_t SizeOfStackCommit; + uint32_t SizeOfHeapReserve; + uint32_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + image_data_directory DataDirectory[16]; + }; + + struct image_nt_headers32 + { + uint32_t Signature; + image_file_header FileHeader; + image_optional_header32 OptionalHeader; + }; + + struct image_optional_header32plus + { + uint16_t Magic; + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; + uint32_t BaseOfCode; + uint64_t ImageBase; + uint32_t SectionAlignment; + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint64_t SizeOfStackReserve; + uint64_t SizeOfStackCommit; + uint64_t SizeOfHeapReserve; + uint64_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + image_data_directory DataDirectory[16]; + }; + + struct image_nt_headers32plus + { + uint32_t Signature; + image_file_header FileHeader; + image_optional_header32plus OptionalHeader; + }; + + struct image_section_header { + uint8_t Name[8]; // IMAGE_SIZEOF_SHORT_NAME + union + { + uint32_t PhysicalAddress; + uint32_t VirtualSize; + } Misc; + uint32_t VirtualAddress; + uint32_t SizeOfRawData; + uint32_t PointerToRawData; + uint32_t PointerToRelocations; + uint32_t PointerToLinenumbers; + uint16_t NumberOfRelocations; + uint16_t NumberOfLinenumbers; + uint32_t Characteristics; + }; + + struct image_cor20_header + { + uint32_t cb; + uint16_t MajorRuntimeVersion; + uint16_t MinorRuntimeVersion; + image_data_directory MetaData; + uint32_t Flags; + union + { + uint32_t EntryPointToken; + uint32_t EntryPointRVA; + } dummyunionname; + image_data_directory Resources; + image_data_directory StrongNameSignature; + image_data_directory CodeManagerTable; + image_data_directory VTableFixups; + image_data_directory ExportAddressTableJumps; + image_data_directory ManagedNativeHeader; + }; +} diff --git a/natvis/winmd/impl/winmd_reader/schema.h b/natvis/winmd/impl/winmd_reader/schema.h new file mode 100644 index 000000000..c3ba25da5 --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/schema.h @@ -0,0 +1,694 @@ + +namespace winmd::reader +{ + struct TypeRef : row_base + { + using row_base::row_base; + + auto ResolutionScope() const + { + return get_coded_index(0); + } + + auto TypeName() const + { + return get_string(1); + } + + auto TypeNamespace() const + { + return get_string(2); + } + + auto CustomAttribute() const; + }; + + struct CustomAttribute : row_base + { + using row_base::row_base; + + auto Parent() const + { + return get_coded_index(0); + } + + auto Type() const + { + return get_coded_index(1); + } + + auto Value() const; + + auto TypeNamespaceAndName() const; + }; + + struct TypeDef : row_base + { + using row_base::row_base; + + auto Flags() const + { + return TypeAttributes{{ get_value(0) }}; + } + + auto TypeName() const + { + return get_string(1); + } + + auto TypeNamespace() const + { + return get_string(2); + } + + auto Extends() const + { + return get_coded_index(3); + } + + auto FieldList() const; + auto MethodList() const; + + auto CustomAttribute() const; + auto InterfaceImpl() const; + auto GenericParam() const; + auto PropertyList() const; + auto EventList() const; + auto MethodImplList() const; + + auto EnclosingType() const; + + bool is_enum() const; + auto get_enum_definition() const; + }; + + struct MethodDef : row_base + { + using row_base::row_base; + + auto RVA() const + { + return get_value(0); + } + + auto ImplFlags() const + { + return MethodImplAttributes{{ get_value(1) }}; + } + + auto Flags() const + { + return MethodAttributes{{ get_value(2) }}; + } + + auto Name() const + { + return get_string(3); + } + + MethodDefSig Signature() const + { + auto cursor = get_blob(4); + return{ get_table(), cursor }; + } + + auto ParamList() const; + auto CustomAttribute() const; + auto Parent() const; + auto GenericParam() const; + + bool SpecialName() const + { + return Flags().SpecialName(); + } + }; + + struct MemberRef : row_base + { + using row_base::row_base; + + auto Class() const + { + return get_coded_index(0); + } + + auto Name() const + { + return get_string(1); + } + + MethodDefSig MethodSignature() const + { + auto cursor = get_blob(2); + return{ get_table(), cursor }; + } + + auto CustomAttribute() const; + }; + + struct Module : row_base + { + using row_base::row_base; + + auto Name() const + { + return get_string(1); + } + + auto CustomAttribute() const; + }; + + struct Field : row_base + { + using row_base::row_base; + + auto Flags() const + { + return FieldAttributes{{ get_value(0) }}; + } + + auto Name() const + { + return get_string(1); + } + + auto Signature() const + { + auto cursor = get_blob(2); + return FieldSig{ get_table(), cursor }; + } + + auto CustomAttribute() const; + auto Constant() const; + auto Parent() const; + auto FieldMarshal() const; + }; + + struct Param : row_base + { + using row_base::row_base; + + auto Flags() const + { + return ParamAttributes{{ get_value(0) }}; + } + + auto Sequence() const + { + return get_value(1); + } + + auto Name() const + { + return get_string(2); + } + + auto CustomAttribute() const; + auto Constant() const; + auto FieldMarshal() const; + }; + + struct InterfaceImpl : row_base + { + using row_base::row_base; + + auto Class() const; + + auto Interface() const + { + return get_coded_index(1); + } + + auto CustomAttribute() const; + }; + + struct Constant : row_base + { + using row_base::row_base; + + using constant_type = std::variant; + + auto Type() const + { + return get_value(0); + } + + auto Parent() const + { + return get_coded_index(1); + } + + auto ValueBoolean() const; + auto ValueChar() const; + auto ValueInt8() const; + auto ValueUInt8() const; + auto ValueInt16() const; + auto ValueUInt16() const; + auto ValueInt32() const; + auto ValueUInt32() const; + auto ValueInt64() const; + auto ValueUInt64() const; + auto ValueFloat32() const; + auto ValueFloat64() const; + auto ValueString() const; + auto ValueClass() const; + + constant_type Value() const; + }; + + struct FieldMarshal : row_base + { + using row_base::row_base; + + auto Parent() const + { + return get_coded_index(0); + } + }; + + struct TypeSpec : row_base + { + using row_base::row_base; + + TypeSpecSig Signature() const + { + auto cursor = get_blob(0); + return{ get_table(), cursor }; + } + + auto CustomAttribute() const; + }; + + struct DeclSecurity : row_base + { + using row_base::row_base; + }; + + struct ClassLayout : row_base + { + using row_base::row_base; + + auto PackingSize() const + { + return get_value(0); + } + + auto ClassSize() const + { + return get_value(1); + } + + auto Parent() const; + }; + + struct FieldLayout : row_base + { + using row_base::row_base; + }; + + struct StandAloneSig : row_base + { + using row_base::row_base; + + auto CustomAttribute() const; + }; + + struct EventMap : row_base + { + using row_base::row_base; + + auto Parent() const; + auto EventList() const; + }; + + struct Event : row_base + { + using row_base::row_base; + + auto EventFlags() const + { + return EventAttributes{{ get_value(0) }}; + } + + auto Name() const + { + return get_string(1); + } + + auto EventType() const + { + return get_coded_index(2); + } + + auto MethodSemantic() const; + auto Parent() const; + auto CustomAttribute() const; + }; + + struct PropertyMap : row_base + { + using row_base::row_base; + + auto Parent() const; + auto PropertyList() const; + }; + + struct Property : row_base + { + using row_base::row_base; + + auto Flags() const + { + return PropertyAttributes{{ get_value(0) }}; + } + + auto Name() const + { + return get_string(1); + } + + PropertySig Type() const + { + auto cursor = get_blob(2); + return{ get_table(), cursor }; + } + + auto MethodSemantic() const; + auto Parent() const; + auto Constant() const; + auto CustomAttribute() const; + }; + + struct MethodSemantics : row_base + { + using row_base::row_base; + + auto Semantic() const + { + return MethodSemanticsAttributes{{ get_value(0) }}; + } + + auto Method() const; + + auto Association() const + { + return get_coded_index(2); + } + }; + + struct MethodImpl : row_base + { + using row_base::row_base; + + auto Class() const; + + auto MethodBody() const + { + return get_coded_index(1); + } + + auto MethodDeclaration() const + { + return get_coded_index(2); + } + }; + + struct ModuleRef : row_base + { + using row_base::row_base; + + auto CustomAttribute() const; + }; + + struct ImplMap : row_base + { + using row_base::row_base; + }; + + struct FieldRVA : row_base + { + using row_base::row_base; + }; + + struct Assembly : row_base + { + using row_base::row_base; + + auto HashAlgId() const + { + return get_value(0); + } + + auto Version() const; + + auto Flags() const + { + return AssemblyAttributes{{ get_value(2) }}; + } + + auto PublicKey() const + { + return get_blob(3); + } + + auto Name() const + { + return get_string(4); + } + + auto Culture() const + { + return get_string(5); + } + + auto CustomAttribute() const; + }; + + struct AssemblyProcessor : row_base + { + using row_base::row_base; + + auto Processor() const + { + return get_value(0); + } + }; + + struct AssemblyOS : row_base + { + using row_base::row_base; + + auto OSPlatformId() const + { + return get_value(0); + } + + auto OSMajorVersion() const + { + return get_value(1); + } + + auto OSMinorVersion() const + { + return get_value(2); + } + }; + + struct AssemblyRef : row_base + { + using row_base::row_base; + + auto Version() const; + + auto Flags() const + { + return AssemblyAttributes{{ get_value(1) }}; + } + + auto PublicKeyOrToken() const + { + return get_blob(2); + } + + auto Name() const + { + return get_string(3); + } + + auto Culture() const + { + return get_string(4); + } + + auto HashValue() const + { + return get_string(5); + } + + auto CustomAttribute() const; + }; + + struct AssemblyRefProcessor : row_base + { + using row_base::row_base; + + auto Processor() const + { + return get_value(0); + } + + auto AssemblyRef() const; + }; + + struct AssemblyRefOS : row_base + { + using row_base::row_base; + + auto OSPlatformId() const + { + return get_value(0); + } + + auto OSMajorVersion() const + { + return get_value(1); + } + + auto OSMinorVersion() const + { + return get_value(2); + } + + auto AssemblyRef() const; + }; + + struct File : row_base + { + using row_base::row_base; + + auto CustomAttribute() const; + }; + + struct ExportedType : row_base + { + using row_base::row_base; + + auto CustomAttribute() const; + }; + + struct ManifestResource : row_base + { + using row_base::row_base; + + auto CustomAttribute() const; + }; + + struct NestedClass : row_base + { + using row_base::row_base; + + TypeDef NestedType() const; + TypeDef EnclosingType() const; + }; + + struct GenericParam : row_base + { + using row_base::row_base; + + auto Number() const + { + return get_value(0); + } + + auto Flags() const + { + return GenericParamAttributes{{ get_value(1) }}; + } + + auto Owner() const + { + return get_coded_index(2); + } + + auto Name() const + { + return get_string(3); + } + + auto CustomAttribute() const; + }; + + struct MethodSpec : row_base + { + using row_base::row_base; + + auto CustomAttribute() const; + }; + + struct GenericParamConstraint : row_base + { + using row_base::row_base; + + auto CustomAttribute() const; + }; + + inline bool operator<(coded_index const& left, CustomAttribute const& right) noexcept + { + return left < right.Parent(); + } + + inline bool operator<(CustomAttribute const& left, coded_index const& right) noexcept + { + return left.Parent() < right; + } + + inline bool operator<(coded_index const& left, GenericParam const& right) noexcept + { + return left < right.Owner(); + } + + inline bool operator<(GenericParam const& left, coded_index const& right) noexcept + { + return left.Owner() < right; + } + + inline bool operator<(coded_index const& left, Constant const& right) noexcept + { + return left < right.Parent(); + } + + inline bool operator<(Constant const& left, coded_index const& right) noexcept + { + return left.Parent() < right; + } + + inline bool operator<(coded_index const& left, MethodSemantics const& right) noexcept + { + return left < right.Association(); + } + + inline bool operator<(MethodSemantics const& left, coded_index const& right) noexcept + { + 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/natvis/winmd/impl/winmd_reader/signature.h b/natvis/winmd/impl/winmd_reader/signature.h new file mode 100644 index 000000000..d36511554 --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/signature.h @@ -0,0 +1,589 @@ + +namespace winmd::reader +{ + inline uint32_t uncompress_unsigned(byte_view& cursor) + { + auto data = cursor.begin(); + uint32_t value; + uint32_t length; + if ((*data & 0x80) == 0x00) + { + length = 1; + value = *data; + } + else if ((*data & 0xc0) == 0x80) + { + length = 2; + value = (*data++ & 0x3f) << 8; + value |= *data; + } + else if ((*data & 0xe0) == 0xc0) + { + length = 4; + value = (*data++ & 0x1f) << 24; + value |= *data++ << 16; + value |= *data++ << 8; + value |= *data; + } + else + { + impl::throw_invalid("Invalid compressed integer in blob"); + } + cursor = cursor.seek(length); + return value; + } + + template + T uncompress_enum(byte_view& cursor) + { + static_assert(std::is_enum_v); + static_assert(std::is_unsigned_v>); + return static_cast(uncompress_unsigned(cursor)); + } + + template + T read(byte_view& cursor) + { + auto const& result = cursor.as(); + cursor = cursor.seek(sizeof(T)); + return result; + } + + template <> + inline std::string_view read(byte_view& cursor) + { + uint32_t const length = uncompress_unsigned(cursor); + std::string_view result{ reinterpret_cast(cursor.begin()), length }; + cursor = cursor.seek(length); + return result; + } + + struct CustomModSig; + struct FieldSig; + struct GenericTypeInstSig; + struct MethodDefSig; + struct ParamSig; + struct PropertySig; + struct RetTypeSig; + struct TypeSig; + struct TypeSpecSig; + + struct CustomModSig + { + CustomModSig(table_base const* table, byte_view& data) + : m_cmod(uncompress_enum(data)) + , m_type(table, uncompress_unsigned(data)) + { + XLANG_ASSERT(m_cmod == ElementType::CModReqd || m_cmod == ElementType::CModOpt); + } + + ElementType CustomMod() const noexcept + { + return m_cmod; + } + + coded_index Type() const noexcept + { + return m_type; + } + + private: + ElementType m_cmod; + coded_index m_type; + }; + + struct GenericTypeInstSig + { + GenericTypeInstSig(table_base const* table, byte_view& data); + + GenericTypeInstSig(coded_index type, std::vector&& args) + : m_type(type) + , m_generic_arg_count(static_cast(args.size())) + , m_generic_args(std::move(args)) + { + // Probably don't care about m_class_or_value + } + + ElementType ClassOrValueType() const noexcept + { + return m_class_or_value; + } + + coded_index GenericType() const noexcept + { + return m_type; + } + + uint32_t GenericArgCount() const noexcept + { + return m_generic_arg_count; + } + + auto GenericArgs() const noexcept + { + return std::pair{ m_generic_args.cbegin(), m_generic_args.cend() }; + } + + private: + ElementType m_class_or_value{}; + coded_index m_type; + uint32_t m_generic_arg_count; + std::vector m_generic_args; + }; + + inline std::vector parse_cmods(table_base const* table, byte_view& data) + { + std::vector result; + auto cursor = data; + + for (auto element_type = uncompress_enum(cursor); + element_type == ElementType::CModOpt || element_type == ElementType::CModReqd; + element_type = uncompress_enum(cursor)) + { + result.emplace_back(table, data); + cursor = data; + } + return result; + } + + inline bool parse_szarray(table_base const*, byte_view& data) + { + auto cursor = data; + if (uncompress_enum(cursor) == ElementType::SZArray) + { + data = cursor; + return true; + } + 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; + }; + + struct GenericMethodTypeIndex + { + uint32_t index; + }; + + struct TypeSig + { + 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); + } + } + + explicit TypeSig(value_type&& arg) + : m_type(std::move(arg)) + { + // We probably don't actually care about m_element_type. Simply populate m_type + } + + value_type const& Type() const noexcept + { + return m_type; + } + + ElementType element_type() const noexcept + { + return m_element_type; + } + + bool is_szarray() const noexcept + { + 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) + { + auto cursor = data; + return uncompress_enum(cursor); + } + + 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) + { + auto cursor = data; + auto element_type = uncompress_enum(cursor); + if (element_type == ElementType::ByRef) + { + data = cursor; + return true; + } + else + { + XLANG_ASSERT(element_type != ElementType::TypedByRef); + return false; + } + } + + struct ParamSig + { + ParamSig(table_base const* table, byte_view& data) + : m_cmod(parse_cmods(table, data)) + , m_byref(is_by_ref(data)) + , m_type(table, data) + { + } + + auto CustomMod() const noexcept + { + return std::pair{ m_cmod.cbegin(), m_cmod.cend() }; + } + + bool ByRef() const noexcept + { + return m_byref; + } + + TypeSig const& Type() const noexcept + { + return m_type; + } + + private: + std::vector m_cmod; + bool m_byref; + TypeSig m_type; + }; + + struct RetTypeSig + { + RetTypeSig(table_base const* table, byte_view& data) + : m_cmod(parse_cmods(table, data)) + , m_byref(is_by_ref(data)) + { + auto cursor = data; + auto element_type = uncompress_enum(cursor); + if (element_type == ElementType::Void) + { + data = cursor; + } + else + { + m_type.emplace(table, data); + } + } + + auto CustomMod() const noexcept + { + return std::pair{ m_cmod.cbegin(), m_cmod.cend() }; + } + + bool ByRef() const noexcept + { + return m_byref; + } + + TypeSig const& Type() const noexcept + { + return *m_type; + } + + explicit operator bool() const noexcept + { + return m_type.has_value(); + } + + private: + std::vector m_cmod; + bool m_byref; + std::optional m_type; + }; + + struct MethodDefSig + { + MethodDefSig(table_base const* table, byte_view& data) + : m_calling_convention(uncompress_enum(data)) + , m_generic_param_count(enum_mask(m_calling_convention, CallingConvention::Generic) == CallingConvention::Generic ? uncompress_unsigned(data) : 0) + , m_param_count(uncompress_unsigned(data)) + , m_ret_type(table, data) + { + if (m_param_count > data.size()) + { + impl::throw_invalid("Invalid blob array size"); + } + m_params.reserve(m_param_count); + for (uint32_t count = 0; count < m_param_count; ++count) + { + m_params.emplace_back(table, data); + } + } + + CallingConvention CallConvention() const noexcept + { + return m_calling_convention; + } + + uint32_t GenericParamCount() const noexcept + { + return m_generic_param_count; + } + + RetTypeSig const& ReturnType() const noexcept + { + return m_ret_type; + } + + auto Params() const noexcept + { + return std::pair{ m_params.cbegin(), m_params.cend() }; + } + + private: + CallingConvention m_calling_convention; + uint32_t m_generic_param_count; + uint32_t m_param_count; + RetTypeSig m_ret_type; + std::vector m_params; + }; + + struct FieldSig + { + FieldSig(table_base const* table, byte_view& data) + : m_calling_convention(check_convention(data)) + , m_cmod(parse_cmods(table, data)) + , m_type(table, data) + {} + + auto CustomMod() const noexcept + { + return std::pair{ m_cmod.cbegin(), m_cmod.cend() }; + } + + TypeSig const& Type() const noexcept + { + return m_type; + } + + private: + static CallingConvention check_convention(byte_view& data) + { + auto conv = read(data); + if (enum_mask(conv, CallingConvention::Field) != CallingConvention::Field) + { + impl::throw_invalid("Invalid calling convention for field blob"); + } + return conv; + } + CallingConvention m_calling_convention; + std::vector m_cmod; + TypeSig m_type; + }; + + struct PropertySig + { + PropertySig(table_base const* table, byte_view& data) + : m_calling_convention(check_convention(data)) + , m_param_count(uncompress_unsigned(data)) + , m_cmod(parse_cmods(table, data)) + , m_type(table, data) + { + if (m_param_count > data.size()) + { + impl::throw_invalid("Invalid blob array size"); + } + m_params.reserve(m_param_count); + for (uint32_t count = 0; count < m_param_count; ++count) + { + m_params.emplace_back(table, data); + } + } + + TypeSig const& Type() const noexcept + { + return m_type; + } + + CallingConvention CallConvention() const noexcept + { + return m_calling_convention; + } + + private: + static CallingConvention check_convention(byte_view& data) + { + auto conv = read(data); + if (enum_mask(conv, CallingConvention::Property) != CallingConvention::Property) + { + impl::throw_invalid("Invalid calling convention for property blob"); + } + return conv; + } + CallingConvention m_calling_convention; + uint32_t m_param_count; + std::vector m_cmod; + TypeSig m_type; + std::vector m_params; + }; + + struct TypeSpecSig + { + TypeSpecSig(table_base const* table, byte_view& data) + : m_type(ParseType(table, data)) + { + } + + GenericTypeInstSig const& GenericTypeInst() const noexcept + { + return m_type; + } + + private: + static GenericTypeInstSig ParseType(table_base const* table, byte_view& data) + { + [[maybe_unused]] auto element_type = uncompress_enum(data); + XLANG_ASSERT(element_type == ElementType::GenericInst); + return { table, data }; + } + GenericTypeInstSig m_type; + }; + + inline GenericTypeInstSig::GenericTypeInstSig(table_base const* table, byte_view& data) + : m_class_or_value(uncompress_enum(data)) + , m_type(table, uncompress_unsigned(data)) + , m_generic_arg_count(uncompress_unsigned(data)) + { + if (!(m_class_or_value == ElementType::Class || m_class_or_value == ElementType::ValueType)) + { + impl::throw_invalid("Generic type instantiation signatures must begin with either ELEMENT_TYPE_CLASS or ELEMENT_TYPE_VALUE"); + } + + if (m_generic_arg_count > data.size()) + { + impl::throw_invalid("Invalid blob array size"); + } + m_generic_args.reserve(m_generic_arg_count); + for (uint32_t arg = 0; arg < m_generic_arg_count; ++arg) + { + m_generic_args.emplace_back(table, data); + } + } + + inline TypeSig::value_type TypeSig::ParseType(table_base const* table, byte_view& data) + { + auto element_type = uncompress_enum(data); + switch (element_type) + { + case ElementType::Boolean: + case ElementType::Char: + case ElementType::I1: + case ElementType::U1: + case ElementType::I2: + case ElementType::U2: + case ElementType::I4: + case ElementType::U4: + case ElementType::I8: + case ElementType::U8: + case ElementType::R4: + case ElementType::R8: + case ElementType::String: + case ElementType::Object: + case ElementType::U: + case ElementType::I: + case ElementType::Void: + return element_type; + break; + + case ElementType::Class: + case ElementType::ValueType: + return coded_index{ table, uncompress_unsigned(data) }; + break; + + case ElementType::GenericInst: + return GenericTypeInstSig{ table, data }; + break; + + case ElementType::Var: + return GenericTypeIndex{ uncompress_unsigned(data) }; + break; + + case ElementType::MVar: + return GenericMethodTypeIndex{ uncompress_unsigned(data) }; + break; + + default: + impl::throw_invalid("Unrecognized ELEMENT_TYPE encountered"); + break; + } + } +} diff --git a/natvis/winmd/impl/winmd_reader/table.h b/natvis/winmd/impl/winmd_reader/table.h new file mode 100644 index 000000000..20da41d0f --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/table.h @@ -0,0 +1,420 @@ + +namespace winmd::reader +{ + struct database; + struct cache; + + struct table_base + { + explicit table_base(database const* database) noexcept : m_database(database) + { + } + + database const& get_database() const noexcept + { + return *m_database; + } + + uint32_t size() const noexcept + { + return m_row_count; + } + + uint32_t row_size() const noexcept + { + return m_row_size; + } + + uint32_t column_size(uint32_t const column) const noexcept + { + return m_columns[column].size; + } + + template + T get_value(uint32_t const row, uint32_t const column) const + { + static_assert(std::is_enum_v || std::is_integral_v); + uint32_t const data_size = m_columns[column].size; + XLANG_ASSERT(data_size == 1 || data_size == 2 || data_size == 4 || data_size == 8); + XLANG_ASSERT(data_size <= sizeof(T)); + + if (row > size()) + { + impl::throw_invalid("Invalid row index"); + } + + uint8_t const* ptr = m_data + row * m_row_size + m_columns[column].offset; + switch (data_size) + { + case 1: + { + uint8_t temp = *ptr; + return static_cast(temp); + } + case 2: + { + uint16_t temp = *reinterpret_cast(ptr); + return static_cast(temp); + } + case 4: + { + uint32_t temp = *reinterpret_cast(ptr); + return static_cast(temp); + } + default: + { + uint64_t temp = *reinterpret_cast(ptr); + return static_cast(temp); + } + } + } + + private: + + friend database; + + struct column + { + uint8_t offset; + uint8_t size; + }; + + database const* m_database; + uint8_t const* m_data{}; + uint32_t m_row_count{}; + uint8_t m_row_size{}; + std::array m_columns{}; + + void set_row_count(uint32_t const row_count) noexcept + { + XLANG_ASSERT(!m_row_count); + m_row_count = row_count; + } + + void set_columns(uint8_t const a, uint8_t const b = 0, uint8_t const c = 0, uint8_t const d = 0, uint8_t const e = 0, uint8_t const f = 0) noexcept + { + XLANG_ASSERT(a); + XLANG_ASSERT(a <= 8); + XLANG_ASSERT(b <= 8); + XLANG_ASSERT(c <= 8); + XLANG_ASSERT(d <= 8); + XLANG_ASSERT(e <= 8); + XLANG_ASSERT(f <= 8); + + XLANG_ASSERT(!m_row_size); + m_row_size = a + b + c + d + e + f; + XLANG_ASSERT(m_row_size < UINT8_MAX); + + m_columns[0] = { 0, a }; + if (b) { m_columns[1] = { static_cast(a), b }; } + if (c) { m_columns[2] = { static_cast(a + b), c }; } + if (d) { m_columns[3] = { static_cast(a + b + c), d }; } + if (e) { m_columns[4] = { static_cast(a + b + c + d), e }; } + if (f) { m_columns[5] = { static_cast(a + b + c + d + e), f }; } + } + + void set_data(byte_view& view) noexcept + { + XLANG_ASSERT(!m_data); + + if (m_row_count) + { + XLANG_ASSERT(m_row_size); + m_data = view.begin(); + view = view.seek(m_row_count * m_row_size); + } + } + + uint8_t index_size() const noexcept + { + return m_row_count < (1 << 16) ? 2 : 4; + } + }; + + template + struct index_base + { + index_base() noexcept = default; + + index_base(table_base const* const table, T const type, uint32_t const row) noexcept : + m_table{ table }, + m_value{ ((row + 1) << coded_index_bits_v) | static_cast(type) } + { + } + + index_base(table_base const* const table, uint32_t const value) noexcept : + m_table{ table }, + m_value{ value } + { + } + + explicit operator bool() const noexcept + { + return m_value != 0; + } + + T type() const noexcept + { + return static_cast(m_value & ((1 << coded_index_bits_v) - 1)); + } + + uint32_t index() const noexcept + { + return (m_value >> coded_index_bits_v) - 1; + } + + template + Row get_row() const; + + bool operator==(index_base const& other) const noexcept + { + return m_value == other.m_value; + } + + bool operator!=(index_base const& other) const noexcept + { + return !(*this == other); + } + + bool operator<(index_base const& other) const noexcept + { + return m_value < other.m_value; + } + + database const& get_database() const noexcept + { + return m_table->get_database(); + } + + protected: + + table_base const* m_table{}; + uint32_t m_value{}; + }; + + template + struct typed_index : index_base + { + using index_base::index_base; + }; + + template <> struct typed_index : index_base + { + using index_base::index_base; + + auto TypeRef() const; + auto TypeDef() const; + }; + + template + struct coded_index : typed_index + { + coded_index() noexcept = default; + + coded_index(table_base const* const table, T const type, uint32_t const row) noexcept : + typed_index{ table, type, row } + { + } + + coded_index(table_base const* const table, uint32_t const value) noexcept : + typed_index{ table, value } + { + } + }; + + template + struct row_base + { + using iterator_category = std::random_access_iterator_tag; + using value_type = Row; + using difference_type = int32_t; + using pointer = value_type*; + using reference = value_type&; + using const_reference = value_type const&; + + row_base(table_base const* const table, uint32_t const index) noexcept : m_table(table), m_index(index) + { + } + + uint32_t index() const noexcept + { + return m_index; + } + + template + auto coded_index() const noexcept + { + return reader::coded_index{ m_table, index_tag_v, index() }; + } + + template + T get_value(uint32_t const column) const + { + XLANG_ASSERT(*this); + return m_table->get_value(m_index, column); + } + + template + auto get_list(uint32_t const column) const; + + template + auto get_target_row(uint32_t const column) const; + + template + auto get_parent_row() const; + + database const& get_database() const noexcept + { + return m_table->get_database(); + } + + cache const& get_cache() const noexcept + { + return get_database().get_cache(); + } + + reference operator++() noexcept + { + ++m_index; + return static_cast(*this); + } + + value_type operator++(int) noexcept + { + row_base temp{ *this }; + operator++(); + return temp; + } + + reference operator--() noexcept + { + --m_index; + return static_cast(*this); + } + + value_type operator--(int) noexcept + { + row_base temp{ *this }; + operator--(); + return temp; + } + + reference operator+=(difference_type offset) noexcept + { + m_index += offset; + return static_cast(*this); + } + + value_type operator+(difference_type offset) const noexcept + { + return { m_table, m_index + offset }; + } + + reference operator-=(difference_type offset) noexcept + { + return *this += -offset; + } + + value_type operator-(difference_type offset) const noexcept + { + return *this + -offset; + } + + difference_type operator-(row_base const& other) const noexcept + { + XLANG_ASSERT(m_table == other.m_table); + return m_index - other.m_index; + } + + value_type operator[](difference_type offset) const noexcept + { + return { m_table, m_index + offset }; + } + + bool operator==(row_base const& other) const noexcept + { + return (m_table == other.m_table) && (m_index == other.m_index); + } + + bool operator!=(row_base const& other) const noexcept + { + return !(*this == other); + } + + bool operator<(row_base const& other) const noexcept + { + return m_table < other.m_table || (!(other.m_table < m_table) && m_index < other.m_index); + } + + bool operator>(row_base const& other) const noexcept + { + return other < *this; + } + + bool operator<=(row_base const& other) const noexcept + { + return !(other < *this); + } + + bool operator>=(row_base const& other) const noexcept + { + return !(*this < other); + } + + [[nodiscard]] const_reference operator*() const noexcept + { + return static_cast(*this); + } + + explicit operator bool() const noexcept + { + return m_table != nullptr; + } + + protected: + + row_base() noexcept = default; + + std::string_view get_string(uint32_t const column) const; + byte_view get_blob(uint32_t const column) const; + + template + auto get_coded_index(uint32_t const column) const + { + return reader::coded_index{ m_table, m_table->get_value(m_index, column) }; + } + + table_base const* get_table() const noexcept + { + return m_table; + } + + private: + + table_base const* m_table{}; + uint32_t m_index{}; + }; + + template + struct table : table_base + { + explicit table(database const* const database) noexcept : table_base{ database } + { + } + + T begin() const noexcept + { + return { this, 0 }; + } + + T end() const noexcept + { + return { this, size() }; + } + + T operator[](uint32_t const row) const noexcept + { + return { this, row }; + } + }; +} diff --git a/natvis/winmd/impl/winmd_reader/type_helpers.h b/natvis/winmd/impl/winmd_reader/type_helpers.h new file mode 100644 index 000000000..0d891bb7f --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/type_helpers.h @@ -0,0 +1,43 @@ + +namespace winmd::reader +{ + inline std::pair get_type_namespace_and_name(coded_index const& type) + { + if (type.type() == TypeDefOrRef::TypeDef) + { + auto const def = type.TypeDef(); + return { def.TypeNamespace(), def.TypeName() }; + } + else if (type.type() == TypeDefOrRef::TypeRef) + { + auto const ref = type.TypeRef(); + return { ref.TypeNamespace(), ref.TypeName() }; + } + else + { + XLANG_ASSERT(false); + return {}; + } + } + + inline std::pair get_base_class_namespace_and_name(TypeDef const& type) + { + return get_type_namespace_and_name(type.Extends()); + } + + inline auto extends_type(TypeDef type, std::string_view typeNamespace, std::string_view typeName) + { + return get_base_class_namespace_and_name(type) == std::pair(typeNamespace, typeName); + } + + inline bool is_nested(TypeDef const& type) + { + const auto visibility = type.Flags().Visibility(); + return !(visibility == TypeVisibility::Public || visibility == TypeVisibility::NotPublic); + } + + inline bool is_nested(TypeRef const& type) + { + return type.ResolutionScope().type() == ResolutionScope::TypeRef; + } +} diff --git a/natvis/winmd/impl/winmd_reader/view.h b/natvis/winmd/impl/winmd_reader/view.h new file mode 100644 index 000000000..91d841423 --- /dev/null +++ b/natvis/winmd/impl/winmd_reader/view.h @@ -0,0 +1,308 @@ + +namespace winmd::reader +{ + template + auto const& begin(std::pair const& values) noexcept + { + return values.first; + } + + template + auto const& end(std::pair const& values) noexcept + { + return values.second; + } + + template + auto distance(std::pair const& values) noexcept + { + return values.second - values.first; + } + + template + auto equal_range(Container const& container, T const& value) noexcept + { + return std::equal_range(container.begin(), container.end(), value); + } + + template + auto equal_range(Container const& container, T const& value, Compare compare) noexcept + { + return std::equal_range(container.begin(), container.end(), value, compare); + } + + struct byte_view; + inline int32_t uncompress_signed(byte_view& cursor, uint32_t length); + + struct byte_view + { + byte_view() noexcept = default; + byte_view(byte_view const&) noexcept = default; + byte_view& operator=(byte_view const&) noexcept = default; + + byte_view(byte_view&& other) noexcept : + m_first(std::exchange(other.m_first, {})), + m_last(std::exchange(other.m_last, {})) + { + } + + byte_view& operator=(byte_view&& other) noexcept + { + m_first = std::exchange(other.m_first, {}); + m_last = std::exchange(other.m_last, {}); + return *this; + } + + byte_view(uint8_t const* const first, uint8_t const* const last) noexcept : + m_first(first), + m_last(last) + { + } + + auto begin() const noexcept + { + return m_first; + } + + auto end() const noexcept + { + return m_last; + } + + uint32_t size() const noexcept + { + return static_cast(end() - begin()); + } + + explicit operator bool() const noexcept + { + return size() > 0; + } + + byte_view seek(uint32_t const offset) const + { + check_available(offset); + return{ m_first + offset, m_last }; + } + + byte_view sub(uint32_t const offset, uint32_t const size) const + { + check_available(offset + size); + return{ m_first + offset, m_first + offset + size }; + } + + template + T const& as(uint32_t const offset = 0) const + { + check_available(offset + sizeof(T)); + return reinterpret_cast(*(m_first + offset)); + } + + std::string_view as_string(uint32_t const offset = 0) const + { + static_assert(sizeof(uint8_t) == 1); + check_available(offset + 1); + auto const length = as(offset); + if (length == 0) + { + return ""; + } + else if (length == 0xff) + { + return { nullptr, 0 }; + } + else + { + check_available(offset + 1 + length); + return { reinterpret_cast(m_first + offset + 1), length }; + } + } + + 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 + { + check_available(offset + count * sizeof(T)); + return reinterpret_cast(m_first + offset); + } + + private: + + void check_available(uint32_t const offset) const + { + if (m_first + offset > m_last) + { + impl::throw_invalid("Buffer too small"); + } + } + + uint8_t const* m_first{}; + uint8_t const* m_last{}; + }; + + struct file_view : byte_view + { + file_view(file_view const&) = delete; + file_view& operator=(file_view const&) = delete; + file_view(file_view&&) noexcept = default; + file_view& operator=(file_view&&) noexcept = default; + + file_view(std::string_view const& path) : byte_view{ open_file(path) }, m_backed_by_file{ true } + { + } + + file_view(uint8_t const* const first, uint8_t const* const last) noexcept : byte_view{ first, last }, m_backed_by_file{ false } + { + } + + ~file_view() noexcept + { + if (m_backed_by_file) + { +#if defined(_WIN32) + UnmapViewOfFile(begin()); +#else + munmap(const_cast(reinterpret_cast(begin())), size()); +#endif + } + } + + private: + + bool m_backed_by_file; + +#if defined(_WIN32) + struct handle + { + HANDLE value{}; + + ~handle() noexcept + { + if (value) + { + CloseHandle(value); + } + } + + explicit operator bool() const noexcept + { + return value != 0; + } + }; +#endif + + struct file_handle + { +#if defined(_WIN32) + using handle_type = HANDLE; +#else + using handle_type = int; + static constexpr handle_type INVALID_HANDLE_VALUE = -1; +#endif + + handle_type value{ INVALID_HANDLE_VALUE }; + + ~file_handle() noexcept + { + if (value != INVALID_HANDLE_VALUE) + { +#if defined(_WIN32) + CloseHandle(value); +#else + close(value); +#endif + } + } + + explicit operator bool() const noexcept + { + return value != INVALID_HANDLE_VALUE; + } + }; + + static byte_view open_file(std::string_view const& path) + { +#if defined(_WIN32) + auto input = impl::c_str(path); + + auto const input_length = static_cast(path.length() + 1); + int buffer_length = MultiByteToWideChar(CP_UTF8, 0, input, input_length, 0, 0); + std::vector output = std::vector(buffer_length); + int result = MultiByteToWideChar(CP_UTF8, 0, input, input_length, output.data(), buffer_length); + + if (result == 0) + { + switch (GetLastError()) + { + case ERROR_INSUFFICIENT_BUFFER: + impl::throw_invalid("Insufficient buffer size"); + case ERROR_NO_UNICODE_TRANSLATION: + impl::throw_invalid("Untranslatable path"); + default: + impl::throw_invalid("Could not convert path"); + } + } + + file_handle file{ CreateFile2(output.data(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr) }; + + if (!file) + { + impl::throw_invalid("Could not open file '", path, "'"); + } + + LARGE_INTEGER size{}; + GetFileSizeEx(file.value, &size); + + if (!size.QuadPart) + { + return{}; + } + + handle mapping{ CreateFileMappingW(file.value, nullptr, PAGE_READONLY, 0, 0, nullptr) }; + + if (!mapping) + { + impl::throw_invalid("Could not open file '", path, "'"); + } + + auto const first{ static_cast(MapViewOfFile(mapping.value, FILE_MAP_READ, 0, 0, 0)) }; + return{ first, first + size.QuadPart }; +#else + file_handle file{ open(impl::c_str(path), O_RDONLY, 0) }; + if (!file) + { + impl::throw_invalid("Could not open file '", path, "'"); + } + + struct stat st; + int ret = fstat(file.value, &st); + if (ret < 0) + { + impl::throw_invalid("Could not open file '", path, "'"); + } + if (!st.st_size) + { + return{}; + } + +#if defined(__linux__) + auto const flags = MAP_PRIVATE | MAP_POPULATE; +#else + auto const flags = MAP_PRIVATE; +#endif + + auto const first = static_cast(mmap(nullptr, st.st_size, PROT_READ, flags, file.value, 0)); + if (first == MAP_FAILED) + { + impl::throw_invalid("Could not open file '", path, "'"); + } + + return{ first, first + st.st_size }; +#endif + } + }; +} diff --git a/natvis/winmd/winmd_reader.h b/natvis/winmd/winmd_reader.h new file mode 100644 index 000000000..4268abb04 --- /dev/null +++ b/natvis/winmd/winmd_reader.h @@ -0,0 +1,20 @@ +#pragma once + +#include "impl/base.h" +#include "impl/winmd_reader/pe.h" +#include "impl/winmd_reader/view.h" +#include "impl/winmd_reader/enum.h" +#include "impl/winmd_reader/enum_traits.h" +#include "impl/winmd_reader/flags.h" +#include "impl/winmd_reader/table.h" +#include "impl/winmd_reader/index.h" +#include "impl/winmd_reader/signature.h" +#include "impl/winmd_reader/schema.h" +#include "impl/winmd_reader/database.h" +#include "impl/winmd_reader/column.h" +#include "impl/winmd_reader/type_helpers.h" +#include "impl/winmd_reader/key.h" +#include "impl/winmd_reader/cache.h" +#include "impl/winmd_reader/filter.h" +#include "impl/winmd_reader/custom_attribute.h" +#include "impl/winmd_reader/helpers.h" From b63aef1429463185f4257c422b9acb6c8829e3b2 Mon Sep 17 00:00:00 2001 From: Ryan Shepherd Date: Wed, 13 Sep 2023 10:49:37 -0700 Subject: [PATCH 02/16] Partial plumbing --- natvis/cppwinrt_visualizer.cpp | 36 ++++++++++++++++++++++++++++++++-- natvis/object_visualizer.cpp | 31 +++++++++++++++++++++++------ natvis/pch.h | 10 ++++++---- natvis/type_resolver.cpp | 13 +++++++++++- 4 files changed, 77 insertions(+), 13 deletions(-) diff --git a/natvis/cppwinrt_visualizer.cpp b/natvis/cppwinrt_visualizer.cpp index 7aa845e53..e62315e33 100644 --- a/natvis/cppwinrt_visualizer.cpp +++ b/natvis/cppwinrt_visualizer.cpp @@ -118,8 +118,9 @@ void LoadMetadata(DkmProcess* process, WCHAR const* processPath, std::string_vie } } -TypeDef FindType(DkmProcess* process, std::string_view const& typeName) +TypeDef FindSimpleType(DkmProcess* process, std::string_view const& typeName) { + XLANG_ASSERT(typeName.find('<') == std::string_view::npos); auto type = db_cache->find(typeName); if (!type) { @@ -135,8 +136,9 @@ TypeDef FindType(DkmProcess* process, std::string_view const& typeName) return type; } -TypeDef FindType(DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName) +TypeDef FindSimpleType(DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName) { + XLANG_ASSERT(typeName.find('<') == std::string_view::npos); auto type = db_cache->find(typeNamespace, typeName); if (!type) { @@ -148,6 +150,36 @@ TypeDef FindType(DkmProcess* process, std::string_view const& typeNamespace, std return type; } +TypeSig FindType(DkmProcess* process, std::string_view const& typeName) +{ + auto paramIndex = typeName.find('<'); + if (paramIndex == std::string_view::npos) + { + return TypeSig{ FindSimpleType(process, typeName).coded_index() }; + } + else + { + XLANG_ASSERT(typeName.back() == '>'); + return TypeSig{ FindSimpleType(process, typeName.substr(0, paramIndex)).coded_index() }; + // TODO: Assemble the GenericTypeInstSig + } +} + +TypeSig FindType(DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName) +{ + auto paramIndex = typeName.find('<'); + if (paramIndex == std::string_view::npos) + { + return TypeSig{ FindSimpleType(process, typeNamespace, typeName).coded_index() }; + } + else + { + XLANG_ASSERT(typeName.back() == '>'); + return TypeSig{ FindSimpleType(process, typeNamespace, typeName.substr(0, paramIndex)).coded_index() }; + // TODO: Assemble the GenericTypeInstSig + } +} + cppwinrt_visualizer::cppwinrt_visualizer() { try diff --git a/natvis/object_visualizer.cpp b/natvis/object_visualizer.cpp index a0803871a..b949d9d10 100644 --- a/natvis/object_visualizer.cpp +++ b/natvis/object_visualizer.cpp @@ -348,11 +348,11 @@ struct property_type void GetInterfaceData( Microsoft::VisualStudio::Debugger::DkmProcess* process, - coded_index index, + TypeSig const& typeSig, _Inout_ std::vector& propertyData, _Out_ bool& isStringable ){ - auto [type, propIid] = ResolveTypeInterface(process, index); + auto [type, propIid] = ResolveTypeInterface(process, typeSig); if (!type) { return; @@ -493,10 +493,29 @@ void object_visualizer::GetPropertyData() GetTypeProperties(process, std::string_view{ rc.data() + 2, rc.length() - 3 }); } +TypeSig ExpandInterfaceImplForType(coded_index impl, TypeSig const& type) +{ + if (std::holds_alternative>(type.Type())) + { + return TypeSig{ impl }; + } + return TypeSig{ impl }; +} + void object_visualizer::GetTypeProperties(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& type_name) { // TODO: add support for direct generic interface implementations (e.g., key_value_pair) - auto type = FindType(process, type_name); + auto typeSig = FindType(process, type_name); + TypeDef type{}; + if (auto const* index = std::get_if>(&typeSig.Type())) + { + type = index->TypeDef(); + } + else if (auto const* genericInst = std::get_if(&typeSig.Type())) + { + type = genericInst->GenericType().TypeDef(); + } + if (!type) { return; @@ -516,7 +535,7 @@ void object_visualizer::GetTypeProperties(Microsoft::VisualStudio::Debugger::Dkm auto impls = type.InterfaceImpl(); for (auto&& impl : impls) { - GetInterfaceData(process, impl.Interface(), m_propertyData, m_isStringable); + GetInterfaceData(process, ExpandInterfaceImplForType(impl.Interface(), typeSig), m_propertyData, m_isStringable); } } else if (get_category(type) == category::interface_type) @@ -524,9 +543,9 @@ void object_visualizer::GetTypeProperties(Microsoft::VisualStudio::Debugger::Dkm auto impls = type.InterfaceImpl(); for (auto&& impl : impls) { - GetInterfaceData(process, impl.Interface(), m_propertyData, m_isStringable); + GetInterfaceData(process, ExpandInterfaceImplForType(impl.Interface(), typeSig), m_propertyData, m_isStringable); } - GetInterfaceData(process, type.coded_index(), m_propertyData, m_isStringable); + GetInterfaceData(process, typeSig, m_propertyData, m_isStringable); } } diff --git a/natvis/pch.h b/natvis/pch.h index 824d3aac1..8c565c303 100644 --- a/natvis/pch.h +++ b/natvis/pch.h @@ -91,8 +91,10 @@ inline bool starts_with(std::string_view const& value, std::string_view const& m return 0 == value.compare(0, match.size(), match); } -winmd::reader::TypeDef FindType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeName); -winmd::reader::TypeDef FindType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName); +winmd::reader::TypeDef FindSimpleType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeName); +winmd::reader::TypeDef FindSimpleType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName); +winmd::reader::TypeSig FindType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeName); +winmd::reader::TypeSig FindType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName); inline winmd::reader::TypeDef ResolveType(Microsoft::VisualStudio::Debugger::DkmProcess* process, winmd::reader::coded_index index) noexcept { @@ -101,13 +103,13 @@ inline winmd::reader::TypeDef ResolveType(Microsoft::VisualStudio::Debugger::Dkm case winmd::reader::TypeDefOrRef::TypeDef: return index.TypeDef(); case winmd::reader::TypeDefOrRef::TypeRef: - return FindType(process, index.TypeRef().TypeNamespace(), index.TypeRef().TypeName()); + return FindSimpleType(process, index.TypeRef().TypeNamespace(), index.TypeRef().TypeName()); default: //case TypeDefOrRef::TypeSpec: return winmd::reader::find_required(index.TypeSpec().Signature(). GenericTypeInst().GenericType().TypeRef()); } } -std::pair ResolveTypeInterface(Microsoft::VisualStudio::Debugger::DkmProcess* process, winmd::reader::coded_index index); +std::pair ResolveTypeInterface(Microsoft::VisualStudio::Debugger::DkmProcess* process, winmd::reader::TypeSig const& typeSig); void ClearTypeResolver(); diff --git a/natvis/type_resolver.cpp b/natvis/type_resolver.cpp index 17999997f..086ee2f56 100644 --- a/natvis/type_resolver.cpp +++ b/natvis/type_resolver.cpp @@ -278,8 +278,19 @@ static guid generate_guid(coded_index const& type) return set_named_guid_fields(endian_swap(to_guid(calculate_sha1(buffer)))); } -std::pair ResolveTypeInterface(DkmProcess* process, coded_index index) +std::pair ResolveTypeInterface(DkmProcess* process, winmd::reader::TypeSig const& typeSig) { + coded_index index; + if (auto ptrIndex = std::get_if>(&typeSig.Type())) + { + index = *ptrIndex; + } + else if (auto* ptrGeneric = std::get_if(&typeSig.Type())) + { + index = ptrGeneric->GenericType(); + } + + // TODO: Cache on the whole TypeSig, not just the generic index if (auto found = _cache.find(index); found != _cache.end()) { return found->second; From 50e986929bc10d6a178547342fa2903eeb4c7144 Mon Sep 17 00:00:00 2001 From: Ryan Shepherd Date: Wed, 13 Sep 2023 16:45:38 -0700 Subject: [PATCH 03/16] Synthesizing GenericTypeInstSig and generating guid --- natvis/cppwinrt_visualizer.cpp | 74 ++++++++++++++++++++++++++++------ natvis/pch.h | 2 +- natvis/type_resolver.cpp | 68 ++++++++++++++++--------------- 3 files changed, 97 insertions(+), 47 deletions(-) diff --git a/natvis/cppwinrt_visualizer.cpp b/natvis/cppwinrt_visualizer.cpp index e62315e33..fa9b7ba47 100644 --- a/natvis/cppwinrt_visualizer.cpp +++ b/natvis/cppwinrt_visualizer.cpp @@ -150,33 +150,81 @@ TypeDef FindSimpleType(DkmProcess* process, std::string_view const& typeNamespac return type; } -TypeSig FindType(DkmProcess* process, std::string_view const& typeName) +std::vector ParseTypeName(std::string_view name) { - auto paramIndex = typeName.find('<'); - if (paramIndex == std::string_view::npos) + DWORD count; + HSTRING* parts; + auto wide_name = winrt::to_hstring(name); + winrt::check_hresult(::RoParseTypeName(static_cast(get_abi(wide_name)), &count, &parts)); + + winrt::com_array wide_parts{ parts, count, winrt::take_ownership_from_abi }; + std::vector result; + for (auto&& part : wide_parts) { - return TypeSig{ FindSimpleType(process, typeName).coded_index() }; + result.push_back(winrt::to_string(part)); } - else + return result; +} + +template sent> +TypeSig ResolveGenericTypePart(DkmProcess* process, iter& it, sent const& end) +{ + constexpr std::pair elementNames[] = { + {"Boolean", ElementType::Boolean}, + {"Int8", ElementType::I1}, + {"Int16", ElementType::I2}, + {"Int32", ElementType::I4}, + {"Int64", ElementType::I8}, + {"UInt8", ElementType::U1}, + {"UInt16", ElementType::U2}, + {"UInt32", ElementType::U4}, + {"UInt64", ElementType::U8}, + {"Single", ElementType::R4}, + {"Double", ElementType::R8}, + {"String", ElementType::String}, + {"Object", ElementType::Object} + }; + std::string_view partName = *it; + auto basic_type_pos = std::find_if(std::begin(elementNames), std::end(elementNames), [&partName](auto&& elem) { return elem.first == partName; }); + if (basic_type_pos != std::end(elementNames)) + { + return TypeSig{ basic_type_pos->second }; + } + + TypeDef type = FindSimpleType(process, partName); + auto tickPos = partName.rfind('`'); + if (tickPos == std::string_view::basic_string_view::npos) { - XLANG_ASSERT(typeName.back() == '>'); - return TypeSig{ FindSimpleType(process, typeName.substr(0, paramIndex)).coded_index() }; - // TODO: Assemble the GenericTypeInstSig + return TypeSig{ type.coded_index() }; } + + int paramCount = 0; + std::from_chars(partName.data() + tickPos + 1, partName.data() + partName.size(), paramCount); + std::vector genericArgs; + for (int i = 0; i < paramCount; ++i) + { + genericArgs.push_back(ResolveGenericTypePart(process, ++it, end)); + } + return TypeSig{ GenericTypeInstSig{ type.coded_index(), std::move(genericArgs) } }; } -TypeSig FindType(DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName) +TypeSig ResolveGenericType(DkmProcess* process, std::string_view genericName) +{ + auto parts = ParseTypeName(genericName); + auto begin = parts.begin(); + return ResolveGenericTypePart(process, begin, parts.end()); +} + +TypeSig FindType(DkmProcess* process, std::string_view const& typeName) { auto paramIndex = typeName.find('<'); if (paramIndex == std::string_view::npos) { - return TypeSig{ FindSimpleType(process, typeNamespace, typeName).coded_index() }; + return TypeSig{ FindSimpleType(process, typeName).coded_index() }; } else { - XLANG_ASSERT(typeName.back() == '>'); - return TypeSig{ FindSimpleType(process, typeNamespace, typeName.substr(0, paramIndex)).coded_index() }; - // TODO: Assemble the GenericTypeInstSig + return ResolveGenericType(process, typeName); } } diff --git a/natvis/pch.h b/natvis/pch.h index 8c565c303..95e561971 100644 --- a/natvis/pch.h +++ b/natvis/pch.h @@ -43,6 +43,7 @@ #include #include #include +#include #ifndef IF_FAIL_RET #define IF_FAIL_RET(expr) { HRESULT _hr = (expr); if(FAILED(_hr)) { return(_hr); } } @@ -94,7 +95,6 @@ inline bool starts_with(std::string_view const& value, std::string_view const& m winmd::reader::TypeDef FindSimpleType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeName); winmd::reader::TypeDef FindSimpleType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName); winmd::reader::TypeSig FindType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeName); -winmd::reader::TypeSig FindType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName); inline winmd::reader::TypeDef ResolveType(Microsoft::VisualStudio::Debugger::DkmProcess* process, winmd::reader::coded_index index) noexcept { diff --git a/natvis/type_resolver.cpp b/natvis/type_resolver.cpp index 086ee2f56..9ee49248b 100644 --- a/natvis/type_resolver.cpp +++ b/natvis/type_resolver.cpp @@ -98,6 +98,18 @@ struct signature_generator } } + static std::string get_signature(GenericTypeInstSig const& type) + { + std::string sig = "pinterface(" + get_guid_signature(type.GenericType()); + for (auto&& arg : type.GenericArgs()) + { + sig += ";"; + sig += get_signature(arg); + } + sig += ")"; + return sig; + } + private: static std::string get_class_signature(TypeDef const& type) { @@ -152,20 +164,8 @@ struct signature_generator case TypeDefOrRef::TypeRef: return get_guid_signature(find_required(type.TypeRef())); default: //case TypeDefOrRef::TypeSpec: - return get_guid_signature(type.TypeSpec().Signature().GenericTypeInst().GenericType()); - } - } - - static std::string get_signature(GenericTypeInstSig const& type) - { - std::string sig = "pinterface(" + get_guid_signature(type.GenericType()); - for (auto&& arg : type.GenericArgs()) - { - sig += ";"; - sig += get_signature(arg); + return get_signature(type.TypeSpec().Signature().GenericTypeInst()); } - sig += ")"; - return sig; } static std::string get_signature(TypeSig::value_type const& type) @@ -266,7 +266,7 @@ static auto calculate_sha1(std::vector const& input) return get_result(intermediate_hash); } -static guid generate_guid(coded_index const& type) +static guid generate_guid(GenericTypeInstSig const& type) { constexpr guid namespace_guid = { 0xd57af411, 0x737b, 0xc042,{ 0xab, 0xae, 0x87, 0x8b, 0x1e, 0x16, 0xad, 0xee } }; constexpr auto namespace_bytes = winrt::impl::to_array(namespace_guid); @@ -284,30 +284,32 @@ std::pair ResolveTypeInterface(DkmProcess* process, winmd if (auto ptrIndex = std::get_if>(&typeSig.Type())) { index = *ptrIndex; + // TODO: Cache on the whole TypeSig, not just the generic index + if (auto found = _cache.find(index); found != _cache.end()) + { + return found->second; + } + + TypeDef type = ResolveType(process, index); + if (!type) + { + return {}; + } + + auto guid = index.type() == TypeDefOrRef::TypeSpec ? + format_guid(generate_guid(index.TypeSpec().Signature().GenericTypeInst())) : format_guid(get_guid(type)); + + auto type_guid = std::pair{ type, guid }; + _cache[index] = type_guid; + return type_guid; } else if (auto* ptrGeneric = std::get_if(&typeSig.Type())) { index = ptrGeneric->GenericType(); + auto guid = format_guid(generate_guid(*ptrGeneric)); + return { index.TypeDef(), guid }; } - - // TODO: Cache on the whole TypeSig, not just the generic index - if (auto found = _cache.find(index); found != _cache.end()) - { - return found->second; - } - - TypeDef type = ResolveType(process, index); - if (!type) - { - return {}; - } - - auto guid = index.type() == TypeDefOrRef::TypeSpec ? - format_guid(generate_guid(index)) : format_guid(get_guid(type)); - - auto type_guid = std::pair{ type, guid }; - _cache[index] = type_guid; - return type_guid; + return {}; }; void ClearTypeResolver() From d222652df263066a2775af83a8037fc1703eb079 Mon Sep 17 00:00:00 2001 From: Ryan Shepherd Date: Thu, 14 Sep 2023 18:29:38 -0700 Subject: [PATCH 04/16] Generic property visualization is working --- natvis/object_visualizer.cpp | 386 ++++++++++++++++++++++++++--------- natvis/object_visualizer.h | 5 + 2 files changed, 300 insertions(+), 91 deletions(-) diff --git a/natvis/object_visualizer.cpp b/natvis/object_visualizer.cpp index b949d9d10..5618a62ba 100644 --- a/natvis/object_visualizer.cpp +++ b/natvis/object_visualizer.cpp @@ -110,7 +110,7 @@ static HRESULT EvaluatePropertyExpression( wchar_t wszEvalText[500]; std::wstring propCast; PCWSTR propField; - if (prop.category < PropertyCategory::Value) + if (IsBuiltIn(prop.category)) { propField = g_categoryData[(int)prop.category].propField; } @@ -278,7 +278,7 @@ static HRESULT CreateChildVisualizedExpression( IF_FAIL_RET(DkmString::Create(prop.displayName.c_str(), pDisplayName.put())); PCWSTR displayType; - if (prop.category < PropertyCategory::Value) + if (IsBuiltIn(prop.category)) { displayType = g_categoryData[(int)prop.category].displayType; } @@ -346,6 +346,281 @@ struct property_type MethodDef set; }; +std::optional GetPropertyCategory( + Microsoft::VisualStudio::Debugger::DkmProcess* process, + TypeSig const& owningType, + TypeSig const& propertyType +) +{ + std::optional propCategory; + if (auto pElementType = std::get_if(&propertyType.Type())) + { + if ((ElementType::Boolean <= *pElementType) && (*pElementType <= ElementType::String)) + { + propCategory = (PropertyCategory)(static_cast::type>(*pElementType) - + static_cast::type>(ElementType::Boolean)); + } + else if (*pElementType == ElementType::Object) + { + // result = PropertyCategory::Class; + } + } + else if (auto pIndex = std::get_if>(&propertyType.Type())) + { + auto type = ResolveType(process, *pIndex); + if (type) + { + if (get_category(type) == category::class_type || get_category(type) == category::interface_type) + { + propCategory = PropertyCategory::Class; + } + else + { + propCategory = PropertyCategory::Value; + } + } + else if (pIndex->type() == TypeDefOrRef::TypeRef) + { + auto typeRef = pIndex->TypeRef(); + if (typeRef.TypeNamespace() == "System" && typeRef.TypeName() == "Guid") + { + propCategory = PropertyCategory::Guid; + } + } + } + else if (auto pGenericInst = std::get_if(&propertyType.Type())) + { + XLANG_ASSERT(get_category(ResolveType(process, pGenericInst->GenericType())) == category::interface_type); + propCategory = PropertyCategory::Class; + } + else if (auto pGenericIndex = std::get_if(&propertyType.Type())) + { + if (auto pOwner = std::get_if(&owningType.Type())) + { + auto const& index = pGenericIndex->index; + auto const& genericArgs = pOwner->GenericArgs(); + propCategory = GetPropertyCategory(process, owningType, genericArgs.first[index]); + } + else + { + NatvisDiagnostic(process, L"Can't resolve GenericTypeIndex property on non-generic Type", NatvisDiagnosticLevel::Warning); + } + } + else + { + NatvisDiagnostic(process, L"Unsupported TypeSig encountered", NatvisDiagnosticLevel::Warning); + } + return propCategory; +} + +struct writer +{ + std::vector generic_params; + + std::string result; + + void write(char c) + { + result.push_back(c); + } + + void write(std::string_view const& str) + { + for (auto c : str) + { + if (c == '.') + { + write(':'); + write(':'); + } + else if (c != '`') + { + write(c); + } + else + { + return; + } + } + } + + void write(ElementType type) + { + switch (type) + { + case ElementType::Boolean: + write("bool"); + break; + case ElementType::Char: + write("wchar_t"); + break; + case ElementType::I1: + write("int8_t"); + break; + case ElementType::U1: + write("uint8_t"); + break; + case ElementType::I2: + write("int16_t"); + break; + case ElementType::U2: + write("uint16_t"); + break; + case ElementType::I4: + write("int32_t"); + break; + case ElementType::U4: + write("uint32_t"); + break; + case ElementType::I8: + write("int64_t"); + break; + case ElementType::U8: + write("uint64_t"); + break; + case ElementType::R4: + write("float"); + break; + case ElementType::R8: + write("double"); + break; + case ElementType::String: + write("winrt::hstring"); + break; + case ElementType::Object: + write("winrt::Windows::Foundation::IInspectable"); + break; + default: + XLANG_ASSERT(false); + break; + }; + } + + void write_namespace_and_type(std::string_view ns, std::string_view name) + { + if (ns == "System") + { + if (name == "Guid") + { + ns = ""; + name = "guid"; + } + } + else if (ns == "Windows.Foundation") + { + if (name == "EventRegistrationToken") + { + ns = ""; + name = "event_token"; + } + else if (name == "HResult") + { + ns = ""; + name = "hresult"; + } + } + else if (ns == "Windows.Foundation.Numerics") + { + if (name == "Matrix3x2") { name = "float3x2"; } + else if (name == "Matrix4x4") { name = "float4x4"; } + else if (name == "Plane") { name = "plane"; } + else if (name == "Quaternion") { name = "quarternion"; } + else if (name == "Vector2") { name = "float2"; } + else if (name == "Vector3") { name = "float3"; } + else if (name == "Vector4") { name = "float4"; } + } + + write("winrt::"); + if (!ns.empty()) + { + write(ns); + write("::"); + } + write(name); + } + + void write(TypeRef const& type) + { + write_namespace_and_type(type.TypeNamespace(), type.TypeName()); + } + + void write(TypeDef const& type) + { + write_namespace_and_type(type.TypeNamespace(), type.TypeName()); + } + + void write(TypeSpec const& type) + { + write(type.Signature().GenericTypeInst()); + } + + void write(coded_index type) + { + switch (type.type()) + { + case TypeDefOrRef::TypeDef: + write(type.TypeDef()); + break; + case TypeDefOrRef::TypeRef: + write(type.TypeRef()); + break; + case TypeDefOrRef::TypeSpec: + write(type.TypeSpec()); + break; + } + } + + void write(GenericTypeInstSig const& type) + { + write(type.GenericType()); + bool first = true; + for (auto&& elem : type.GenericArgs()) + { + if (first) + { + first = false; + } + else + { + write(", "); + } + write(elem); + } + } + + void write(GenericTypeIndex const& var) + { + write(generic_params[var.index]); + } + + void write(GenericMethodTypeIndex const&) + { + // Nothing + } + + void write(TypeSig const& type) + { + std::visit([this](auto&& arg) + { + write(arg); + }, type.Type()); + } +}; + +std::wstring string_to_wstring(std::string_view const& str) +{ + int const size = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.size()), nullptr, 0); + if (size == 0) + { + return {}; + } + + std::wstring result(size, L'?'); + auto size_result = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.size()), result.data(), size); + XLANG_ASSERT(size == size_result); + return result; +} + void GetInterfaceData( Microsoft::VisualStudio::Debugger::DkmProcess* process, TypeSig const& typeSig, @@ -353,6 +628,7 @@ void GetInterfaceData( _Out_ bool& isStringable ){ auto [type, propIid] = ResolveTypeInterface(process, typeSig); + if (!type) { return; @@ -375,106 +651,34 @@ void GetInterfaceData( continue; } - std::optional propCategory; - std::wstring propAbiType; - std::wstring propDisplayType; - - auto retType = method.Signature().ReturnType(); - std::visit(overloaded{ - [&](ElementType type) - { - if ((ElementType::Boolean <= type) && (type <= ElementType::String)) - { - propCategory = (PropertyCategory)(static_cast::type>(type) - - static_cast::type>(ElementType::Boolean)); - } - else if (type == ElementType::Object) - { - //propDisplayType = L"winrt::Windows::Foundation::IInspectable"; - //propCategory = PropertyCategory::Class; - //propAbiType = L"winrt::impl::inspectable_abi*"; - } - }, - [&](coded_index const& index) + std::optional propCategory = GetPropertyCategory(process, typeSig, method.Signature().ReturnType().Type()); + if (propCategory) + { + std::wstring propAbiType; + std::wstring propDisplayType; + if (!IsBuiltIn(*propCategory)) { - auto type = ResolveType(process, index); - if (!type) + writer writer; + if (auto pGenericTypeInst = std::get_if(&typeSig.Type())) { - return; + auto const& genericArgs = pGenericTypeInst->GenericArgs(); + writer.generic_params.assign(genericArgs.first, genericArgs.second); } - - auto typeName = type.TypeName(); - if (typeName == "GUID"sv) + writer.write(method.Signature().ReturnType().Type()); + propDisplayType = string_to_wstring(writer.result); + + if (*propCategory == PropertyCategory::Class) { - propCategory = PropertyCategory::Guid; + propAbiType = L"winrt::impl::inspectable_abi*"; } else { - auto ns = std::string(type.TypeNamespace()); - auto name = std::string(type.TypeName()); - - // Map numeric type names - if (ns == "Windows.Foundation.Numerics") - { - if (name == "Matrix3x2") { name = "float3x2"; } - else if (name == "Matrix4x4") { name = "float4x4"; } - else if (name == "Plane") { name = "plane"; } - else if (name == "Quaternion") { name = "quaternion"; } - else if (name == "Vector2") { name = "float2"; } - else if (name == "Vector3") { name = "float3"; } - else if (name == "Vector4") { name = "float4"; } - } - - // Types come back from winmd files with '.', need to be '::' - // Ex. Windows.Foundation.Uri needs to be Windows::Foundation::Uri - auto fullTypeName = ns + "::" + name; - wchar_t cppTypename[500]; - size_t i, j; - for (i = 0, j = 0; i < (fullTypeName.length() + 1); i++, j++) - { - if (fullTypeName[i] == L'.') - { - cppTypename[j++] = L':'; - cppTypename[j] = L':'; - } - else - { - cppTypename[j] = fullTypeName[i]; - } - } - - propDisplayType = std::wstring(L"winrt::") + cppTypename; - if(get_category(type) == category::class_type) - { - propCategory = PropertyCategory::Class; - propAbiType = L"winrt::impl::inspectable_abi*"; - } - else - { - propCategory = PropertyCategory::Value; - propAbiType = propDisplayType; - } + propAbiType = propDisplayType; } - }, - [&](GenericTypeIndex /*var*/) - { - NatvisDiagnostic(process, L"Generics not yet supported", NatvisDiagnosticLevel::Warning); - }, - [&](GenericMethodTypeIndex /*var*/) - { - NatvisDiagnostic(process, L"Generics not yet supported", NatvisDiagnosticLevel::Warning); - }, - [&](GenericTypeInstSig const& /*type*/) - { - NatvisDiagnostic(process, L"Generics not yet supported", NatvisDiagnosticLevel::Warning); } - }, retType.Type().Type()); - if (propCategory) - { auto propName = method.Name().substr(4); - std::wstring propDisplayName(propName.cbegin(), propName.cend()); - propertyData.push_back({ propIid, propIndex, *propCategory, propAbiType, propDisplayType, propDisplayName }); + propertyData.emplace_back(propIid, propIndex, *propCategory, std::move(propAbiType), std::move(propDisplayType), string_to_wstring(propName)); } } } diff --git a/natvis/object_visualizer.h b/natvis/object_visualizer.h index ad83d79d7..6d1a0f00c 100644 --- a/natvis/object_visualizer.h +++ b/natvis/object_visualizer.h @@ -20,6 +20,11 @@ enum class PropertyCategory Class, }; +inline constexpr bool IsBuiltIn(PropertyCategory value) noexcept +{ + return PropertyCategory::Bool <= value && value < PropertyCategory::Value; +} + enum class ObjectType { Abi, From e436ad2eaaba5451364c5cc1087df3c9516735a3 Mon Sep 17 00:00:00 2001 From: Ryan Shepherd Date: Thu, 14 Sep 2023 19:22:25 -0700 Subject: [PATCH 05/16] Handle generics in InterfaceImpl --- natvis/object_visualizer.cpp | 34 ++++++++++++++++++++++++++++++---- natvis/type_resolver.cpp | 2 +- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/natvis/object_visualizer.cpp b/natvis/object_visualizer.cpp index 5618a62ba..4bbe092a9 100644 --- a/natvis/object_visualizer.cpp +++ b/natvis/object_visualizer.cpp @@ -697,11 +697,37 @@ void object_visualizer::GetPropertyData() GetTypeProperties(process, std::string_view{ rc.data() + 2, rc.length() - 3 }); } +GenericTypeInstSig ReplaceGenericIndices(GenericTypeInstSig const& sig, std::vector const& genericArgs) +{ + std::vector replacementArgs; + for (auto&& arg : sig.GenericArgs()) + { + if (auto pGenericSig = std::get_if(&arg.Type())) + { + replacementArgs.emplace_back(ReplaceGenericIndices(*pGenericSig, genericArgs)); + } + else if (auto pGenericIndex = std::get_if(&arg.Type())) + { + replacementArgs.push_back(genericArgs[pGenericIndex->index]); + } + else + { + replacementArgs.push_back(arg); + } + } + return GenericTypeInstSig{ sig.GenericType(), std::move(replacementArgs) }; +} + TypeSig ExpandInterfaceImplForType(coded_index impl, TypeSig const& type) { - if (std::holds_alternative>(type.Type())) + if (auto pGenericInst = std::get_if(&type.Type())) { - return TypeSig{ impl }; + if (impl.type() == TypeDefOrRef::TypeSpec) + { + auto const& genericArgs = pGenericInst->GenericArgs(); + auto newSig = ReplaceGenericIndices(impl.TypeSpec().Signature().GenericTypeInst(), std::vector{ genericArgs.first, genericArgs.second }); + return TypeSig{ newSig }; + } } return TypeSig{ impl }; } @@ -713,11 +739,11 @@ void object_visualizer::GetTypeProperties(Microsoft::VisualStudio::Debugger::Dkm TypeDef type{}; if (auto const* index = std::get_if>(&typeSig.Type())) { - type = index->TypeDef(); + type = ResolveType(process, *index); } else if (auto const* genericInst = std::get_if(&typeSig.Type())) { - type = genericInst->GenericType().TypeDef(); + type = ResolveType(process, genericInst->GenericType()); } if (!type) diff --git a/natvis/type_resolver.cpp b/natvis/type_resolver.cpp index 9ee49248b..de8af71f4 100644 --- a/natvis/type_resolver.cpp +++ b/natvis/type_resolver.cpp @@ -307,7 +307,7 @@ std::pair ResolveTypeInterface(DkmProcess* process, winmd { index = ptrGeneric->GenericType(); auto guid = format_guid(generate_guid(*ptrGeneric)); - return { index.TypeDef(), guid }; + return { ResolveType(process, index), guid }; } return {}; }; From 39cf543262cd9ebd6a990026c87b42cd5cb51ce7 Mon Sep 17 00:00:00 2001 From: Ryan Shepherd Date: Mon, 16 Sep 2024 11:13:39 -0700 Subject: [PATCH 06/16] Bring local winmd changes up to date with official --- natvis/winmd/impl/base.h | 1 + natvis/winmd/impl/winmd_reader/database.h | 76 +++++++++++------------ natvis/winmd/impl/winmd_reader/flags.h | 6 +- natvis/winmd/impl/winmd_reader/index.h | 6 +- 4 files changed, 45 insertions(+), 44 deletions(-) diff --git a/natvis/winmd/impl/base.h b/natvis/winmd/impl/base.h index 60e54e23e..aa197e030 100644 --- a/natvis/winmd/impl/base.h +++ b/natvis/winmd/impl/base.h @@ -26,6 +26,7 @@ #include #include #include +#include #if defined(_DEBUG) #define XLANG_DEBUG diff --git a/natvis/winmd/impl/winmd_reader/database.h b/natvis/winmd/impl/winmd_reader/database.h index c65de70f5..9a6b50542 100644 --- a/natvis/winmd/impl/winmd_reader/database.h +++ b/natvis/winmd/impl/winmd_reader/database.h @@ -119,44 +119,44 @@ namespace winmd::reader initialize(); } - table TypeRef{ this }; - table GenericParamConstraint{ this }; - table TypeSpec{ this }; - table TypeDef{ this }; - table CustomAttribute{ this }; - table MethodDef{ this }; - table MemberRef{ this }; - table Module{ this }; - table Param{ this }; - table InterfaceImpl{ this }; - table Constant{ this }; - table Field{ this }; - table FieldMarshal{ this }; - table DeclSecurity{ this }; - table ClassLayout{ this }; - table FieldLayout{ this }; - table StandAloneSig{ this }; - table EventMap{ this }; - table Event{ this }; - table PropertyMap{ this }; - table Property{ this }; - table MethodSemantics{ this }; - table MethodImpl{ this }; - table ModuleRef{ this }; - table ImplMap{ this }; - table FieldRVA{ this }; - table Assembly{ this }; - table AssemblyProcessor{ this }; - table AssemblyOS{ this }; - table AssemblyRef{ this }; - table AssemblyRefProcessor{ this }; - table AssemblyRefOS{ this }; - table File{ this }; - table ExportedType{ this }; - table ManifestResource{ this }; - table NestedClass{ this }; - table GenericParam{ this }; - table MethodSpec{ this }; + table TypeRef{ this }; + table GenericParamConstraint{ this }; + table TypeSpec{ this }; + table TypeDef{ this }; + table CustomAttribute{ this }; + table MethodDef{ this }; + table MemberRef{ this }; + table Module{ this }; + table Param{ this }; + table InterfaceImpl{ this }; + table Constant{ this }; + table Field{ this }; + table FieldMarshal{ this }; + table DeclSecurity{ this }; + table ClassLayout{ this }; + table FieldLayout{ this }; + table StandAloneSig{ this }; + table EventMap{ this }; + table Event{ this }; + table PropertyMap{ this }; + table Property{ this }; + table MethodSemantics{ this }; + table MethodImpl{ this }; + table ModuleRef{ this }; + table ImplMap{ this }; + table FieldRVA{ this }; + table Assembly{ this }; + table AssemblyProcessor{ this }; + table AssemblyOS{ this }; + table AssemblyRef{ this }; + table AssemblyRefProcessor{ this }; + table AssemblyRefOS{ this }; + table File{ this }; + table ExportedType{ this }; + table ManifestResource{ this }; + table NestedClass{ this }; + table GenericParam{ this }; + table MethodSpec{ this }; template table const& get_table() const noexcept; diff --git a/natvis/winmd/impl/winmd_reader/flags.h b/natvis/winmd/impl/winmd_reader/flags.h index 62dee7fc4..6ae8ebfe2 100644 --- a/natvis/winmd/impl/winmd_reader/flags.h +++ b/natvis/winmd/impl/winmd_reader/flags.h @@ -303,7 +303,7 @@ namespace winmd::reader struct MethodImplAttributes : impl::AttributesBase { - constexpr CodeType CodeType() const noexcept + constexpr reader::CodeType CodeType() const noexcept { return get_enum(CodeType_mask); } @@ -311,7 +311,7 @@ namespace winmd::reader { set_enum(arg, CodeType_mask); } - constexpr Managed Managed() const noexcept + constexpr reader::Managed Managed() const noexcept { return get_enum(Managed_mask); } @@ -600,7 +600,7 @@ namespace winmd::reader { set_bit(arg, WindowsRuntime_bit); } - constexpr StringFormat StringFormat() const noexcept + constexpr reader::StringFormat StringFormat() const noexcept { return get_enum(StringFormat_mask); } diff --git a/natvis/winmd/impl/winmd_reader/index.h b/natvis/winmd/impl/winmd_reader/index.h index 6a7fc2263..26de81f10 100644 --- a/natvis/winmd/impl/winmd_reader/index.h +++ b/natvis/winmd/impl/winmd_reader/index.h @@ -48,9 +48,9 @@ namespace winmd::reader { using index_base::index_base; - TypeDef TypeDef() const; - TypeRef TypeRef() const; - TypeSpec TypeSpec() const; + reader::TypeDef TypeDef() const; + reader::TypeRef TypeRef() const; + reader::TypeSpec TypeSpec() const; auto CustomAttribute() const; }; } From 3f0895e50391f1fea3acac1e434dd94b54dd1126 Mon Sep 17 00:00:00 2001 From: Ryan Shepherd Date: Mon, 16 Sep 2024 19:34:18 -0700 Subject: [PATCH 07/16] Try to clean some analysis warnings. --- natvis/cppwinrt_visualizer.cpp | 3 ++- natvis/cppwinrt_visualizer.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/natvis/cppwinrt_visualizer.cpp b/natvis/cppwinrt_visualizer.cpp index fa9b7ba47..491cb74d0 100644 --- a/natvis/cppwinrt_visualizer.cpp +++ b/natvis/cppwinrt_visualizer.cpp @@ -273,7 +273,7 @@ cppwinrt_visualizer::~cppwinrt_visualizer() HRESULT cppwinrt_visualizer::EvaluateVisualizedExpression( _In_ DkmVisualizedExpression* pVisualizedExpression, - _Deref_out_ DkmEvaluationResult** ppResultObject + _COM_Outptr_result_maybenull_ DkmEvaluationResult** ppResultObject ) { try @@ -313,6 +313,7 @@ HRESULT cppwinrt_visualizer::EvaluateVisualizedExpression( // unrecognized type NatvisDiagnostic(pVisualizedExpression, std::wstring(L"Unrecognized type: ") + (LPWSTR)bstrTypeName, NatvisDiagnosticLevel::Error); + *ppResultObject = nullptr; return S_OK; } diff --git a/natvis/cppwinrt_visualizer.h b/natvis/cppwinrt_visualizer.h index 5f12ec39a..924dc3cbc 100644 --- a/natvis/cppwinrt_visualizer.h +++ b/natvis/cppwinrt_visualizer.h @@ -8,7 +8,7 @@ struct cppwinrt_visualizer : winrt::implements Date: Mon, 16 Sep 2024 19:34:39 -0700 Subject: [PATCH 08/16] Write template brackets --- natvis/object_visualizer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/natvis/object_visualizer.cpp b/natvis/object_visualizer.cpp index 4bbe092a9..0c82ff1a5 100644 --- a/natvis/object_visualizer.cpp +++ b/natvis/object_visualizer.cpp @@ -574,6 +574,7 @@ struct writer { write(type.GenericType()); bool first = true; + write("<"); for (auto&& elem : type.GenericArgs()) { if (first) @@ -586,6 +587,7 @@ struct writer } write(elem); } + write(">"); } void write(GenericTypeIndex const& var) From 8e44c6f4e792c300a68a291f887eb4bf1c933b06 Mon Sep 17 00:00:00 2001 From: Ryan Shepherd Date: Thu, 19 Sep 2024 19:04:24 -0700 Subject: [PATCH 09/16] Remove unused struct --- natvis/object_visualizer.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/natvis/object_visualizer.cpp b/natvis/object_visualizer.cpp index 0c82ff1a5..2e0eb3971 100644 --- a/natvis/object_visualizer.cpp +++ b/natvis/object_visualizer.cpp @@ -340,12 +340,6 @@ static HRESULT CreateChildVisualizedExpression( return S_OK; } -struct property_type -{ - MethodDef get; - MethodDef set; -}; - std::optional GetPropertyCategory( Microsoft::VisualStudio::Debugger::DkmProcess* process, TypeSig const& owningType, From 7be57072bc9ccf95ad30b08320c058f3ebeaa90e Mon Sep 17 00:00:00 2001 From: Ryan Shepherd Date: Mon, 23 Sep 2024 11:22:07 -0700 Subject: [PATCH 10/16] Fix incorrect FindSimpleType --- natvis/cppwinrt_visualizer.cpp | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/natvis/cppwinrt_visualizer.cpp b/natvis/cppwinrt_visualizer.cpp index 491cb74d0..7da07f468 100644 --- a/natvis/cppwinrt_visualizer.cpp +++ b/natvis/cppwinrt_visualizer.cpp @@ -11,8 +11,11 @@ using namespace std::filesystem; using namespace winrt; using namespace winmd::reader; -std::vector db_files; -std::unique_ptr db_cache; +namespace natvis +{ + std::vector db_files; + std::unique_ptr db_cache; +} void MetadataDiagnostic(DkmProcess* process, std::wstring const& status, std::filesystem::path const& path) { @@ -103,10 +106,10 @@ void LoadMetadata(DkmProcess* process, WCHAR const* processPath, std::string_vie auto const path_string = winmd_path.string(); - if (std::find(db_files.begin(), db_files.end(), path_string) == db_files.end()) + if (std::find(natvis::db_files.begin(), natvis::db_files.end(), path_string) == natvis::db_files.end()) { - db_cache->add_database(path_string, [](TypeDef const& type) { return type.Flags().WindowsRuntime(); }); - db_files.push_back(path_string); + natvis::db_cache->add_database(path_string, [](TypeDef const& type) { return type.Flags().WindowsRuntime(); }); + natvis::db_files.push_back(path_string); } } auto pos = probe_file.rfind('.'); @@ -121,12 +124,12 @@ void LoadMetadata(DkmProcess* process, WCHAR const* processPath, std::string_vie TypeDef FindSimpleType(DkmProcess* process, std::string_view const& typeName) { XLANG_ASSERT(typeName.find('<') == std::string_view::npos); - auto type = db_cache->find(typeName); + auto type = natvis::db_cache->find(typeName); if (!type) { auto processPath = process->Path()->Value(); LoadMetadata(process, processPath, typeName); - type = db_cache->find(typeName); + type = natvis::db_cache->find(typeName); if (!type) { NatvisDiagnostic(process, @@ -139,13 +142,13 @@ TypeDef FindSimpleType(DkmProcess* process, std::string_view const& typeName) TypeDef FindSimpleType(DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName) { XLANG_ASSERT(typeName.find('<') == std::string_view::npos); - auto type = db_cache->find(typeNamespace, typeName); + auto type = natvis::db_cache->find(typeNamespace, typeName); if (!type) { std::string fullName(typeNamespace); fullName.append("."); fullName.append(typeName); - FindType(process, fullName); + FindSimpleType(process, fullName); } return type; } @@ -242,10 +245,10 @@ cppwinrt_visualizer::cppwinrt_visualizer() { if (std::filesystem::is_regular_file(file)) { - db_files.push_back(file.path().string()); + natvis::db_files.push_back(file.path().string()); } } - db_cache.reset(new cache(db_files, [](TypeDef const& type) { return type.Flags().WindowsRuntime(); })); + natvis::db_cache.reset(new cache(natvis::db_files, [](TypeDef const& type) { return type.Flags().WindowsRuntime(); })); } catch (...) { @@ -267,8 +270,8 @@ cppwinrt_visualizer::cppwinrt_visualizer() cppwinrt_visualizer::~cppwinrt_visualizer() { ClearTypeResolver(); - db_files.clear(); - db_cache.reset(); + natvis::db_files.clear(); + natvis::db_cache.reset(); } HRESULT cppwinrt_visualizer::EvaluateVisualizedExpression( From a20fba73e4ae759bdebe78a00b73627482c1b48a Mon Sep 17 00:00:00 2001 From: Ryan Shepherd Date: Fri, 31 Jan 2025 12:50:30 -0800 Subject: [PATCH 11/16] Move from local prototype of winmd nuget to new official version --- natvis/cppwinrtvisualizer.vcxproj | 6 +- natvis/packages.config | 1 + natvis/winmd/impl/base.h | 72 -- natvis/winmd/impl/winmd_reader/cache.h | 257 ------- natvis/winmd/impl/winmd_reader/column.h | 551 -------------- .../impl/winmd_reader/custom_attribute.h | 385 ---------- natvis/winmd/impl/winmd_reader/database.h | 624 ---------------- natvis/winmd/impl/winmd_reader/enum.h | 329 --------- natvis/winmd/impl/winmd_reader/enum_traits.h | 170 ----- natvis/winmd/impl/winmd_reader/filter.h | 136 ---- natvis/winmd/impl/winmd_reader/flags.h | 660 ----------------- natvis/winmd/impl/winmd_reader/helpers.h | 129 ---- natvis/winmd/impl/winmd_reader/index.h | 56 -- natvis/winmd/impl/winmd_reader/key.h | 213 ------ natvis/winmd/impl/winmd_reader/pe.h | 163 ---- natvis/winmd/impl/winmd_reader/schema.h | 694 ------------------ natvis/winmd/impl/winmd_reader/signature.h | 589 --------------- natvis/winmd/impl/winmd_reader/table.h | 420 ----------- natvis/winmd/impl/winmd_reader/type_helpers.h | 43 -- natvis/winmd/impl/winmd_reader/view.h | 308 -------- natvis/winmd/winmd_reader.h | 20 - 21 files changed, 5 insertions(+), 5821 deletions(-) delete mode 100644 natvis/winmd/impl/base.h delete mode 100644 natvis/winmd/impl/winmd_reader/cache.h delete mode 100644 natvis/winmd/impl/winmd_reader/column.h delete mode 100644 natvis/winmd/impl/winmd_reader/custom_attribute.h delete mode 100644 natvis/winmd/impl/winmd_reader/database.h delete mode 100644 natvis/winmd/impl/winmd_reader/enum.h delete mode 100644 natvis/winmd/impl/winmd_reader/enum_traits.h delete mode 100644 natvis/winmd/impl/winmd_reader/filter.h delete mode 100644 natvis/winmd/impl/winmd_reader/flags.h delete mode 100644 natvis/winmd/impl/winmd_reader/helpers.h delete mode 100644 natvis/winmd/impl/winmd_reader/index.h delete mode 100644 natvis/winmd/impl/winmd_reader/key.h delete mode 100644 natvis/winmd/impl/winmd_reader/pe.h delete mode 100644 natvis/winmd/impl/winmd_reader/schema.h delete mode 100644 natvis/winmd/impl/winmd_reader/signature.h delete mode 100644 natvis/winmd/impl/winmd_reader/table.h delete mode 100644 natvis/winmd/impl/winmd_reader/type_helpers.h delete mode 100644 natvis/winmd/impl/winmd_reader/view.h delete mode 100644 natvis/winmd/winmd_reader.h diff --git a/natvis/cppwinrtvisualizer.vcxproj b/natvis/cppwinrtvisualizer.vcxproj index a244fa2ed..b77013ac4 100644 --- a/natvis/cppwinrtvisualizer.vcxproj +++ b/natvis/cppwinrtvisualizer.vcxproj @@ -1,5 +1,6 @@ + Debug @@ -130,7 +131,7 @@ Disabled false VSDEBUGENG_USE_CPP11_SCOPED_ENUMS;_DEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - .\winmd;$(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories) + $(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories) stdcpp20 pch.h @@ -210,7 +211,7 @@ true false VSDEBUGENG_USE_CPP11_SCOPED_ENUMS;NDEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - .\winmd;$(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories) + $(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories) stdcpp20 pch.h Guard @@ -320,5 +321,6 @@ + \ No newline at end of file diff --git a/natvis/packages.config b/natvis/packages.config index 9de65ed35..7907cba44 100644 --- a/natvis/packages.config +++ b/natvis/packages.config @@ -2,4 +2,5 @@ + \ No newline at end of file diff --git a/natvis/winmd/impl/base.h b/natvis/winmd/impl/base.h deleted file mode 100644 index aa197e030..000000000 --- a/natvis/winmd/impl/base.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#if defined(_WIN32) -#include -#include -#else -#include -#include -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_DEBUG) -#define XLANG_DEBUG -#define XLANG_ASSERT assert -#else -#define XLANG_ASSERT(expression) ((void)0) -#endif - -namespace winmd -{ - using namespace std::literals; - - namespace impl - { - [[noreturn]] inline void throw_invalid(std::string const& message) - { - throw std::invalid_argument(message); - } - - template - [[noreturn]] inline void throw_invalid(std::string message, T const&... args) - { - (message.append(args), ...); - throw std::invalid_argument(message); - } - - template - auto c_str(std::basic_string_view const& view) noexcept - { - if (*(view.data() + view.size())) - { - std::terminate(); - } - - return view.data(); - } - - inline bool starts_with(std::string_view const& value, std::string_view const& match) noexcept - { - return 0 == value.compare(0, match.size(), match); - } - } -} diff --git a/natvis/winmd/impl/winmd_reader/cache.h b/natvis/winmd/impl/winmd_reader/cache.h deleted file mode 100644 index 6d113e5a6..000000000 --- a/natvis/winmd/impl/winmd_reader/cache.h +++ /dev/null @@ -1,257 +0,0 @@ - -namespace winmd::reader -{ - struct cache - { - cache() = default; - cache(cache const&) = delete; - cache& operator=(cache const&) = delete; - - template - explicit cache(C const& files, TypeFilter filter) - { - for (auto&& file : files) - { - auto& db = m_databases.emplace_back(file, this); - - for (auto&& type : db.TypeDef) - { - if (type.Flags().value == 0 || is_nested(type) || !filter(type)) - { - continue; - } - - 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) - { - for (auto&&[name, type] : members.types) - { - add_type_to_members(type, members); - } - } - } - - template - explicit cache(C const& files) : cache{ files, default_type_filter{} } - { - } - - template - explicit cache(std::string const& file, TypeFilter filter) : cache{ std::vector{ file }, filter } - { - } - - explicit cache(std::string const& file) : cache{ std::vector{ file }, default_type_filter{} } - { - } - - struct default_type_filter - { - bool operator()(TypeDef const&) const noexcept - { - return true; - } - }; - - TypeDef find(std::string_view const& type_namespace, std::string_view const& type_name) const noexcept - { - auto ns = m_namespaces.find(type_namespace); - - if (ns == m_namespaces.end()) - { - return {}; - } - - auto type = ns->second.types.find(type_name); - - if (type == ns->second.types.end()) - { - return {}; - } - - return type->second; - } - - TypeDef find(std::string_view const& type_string) const - { - auto pos = type_string.rfind('.'); - - if (pos == std::string_view::npos) - { - impl::throw_invalid("Type '", type_string, "' is missing a namespace qualifier"); - } - - return find(type_string.substr(0, pos), type_string.substr(pos + 1, type_string.size())); - } - - TypeDef find_required(std::string_view const& type_namespace, std::string_view const& type_name) const - { - auto definition = find(type_namespace, type_name); - - if (!definition) - { - impl::throw_invalid("Type '", type_namespace, ".", type_name, "' could not be found"); - } - - return definition; - } - - TypeDef find_required(std::string_view const& type_string) const - { - auto pos = type_string.rfind('.'); - - if (pos == std::string_view::npos) - { - impl::throw_invalid("Type '", type_string, "' is missing a namespace qualifier"); - } - - return find_required(type_string.substr(0, pos), type_string.substr(pos + 1, type_string.size())); - } - - auto const& databases() const noexcept - { - return m_databases; - } - - auto const& namespaces() const noexcept - { - return m_namespaces; - } - - void remove_type(std::string_view const& ns, std::string_view const& name) - { - auto m = m_namespaces.find(ns); - if (m == m_namespaces.end()) - { - return; - } - auto& members = m->second; - - auto remove = [&](auto&& collection, auto&& name) - { - auto pos = std::find_if(collection.begin(), collection.end(), [&](auto&& type) - { - return type.TypeName() == name; - }); - - if (pos != collection.end()) - { - collection.erase(pos); - } - }; - - remove(members.interfaces, name); - remove(members.classes, name); - remove(members.enums, name); - remove(members.structs, name); - remove(members.delegates, name); - } - - // This won't invalidate any existing database or row_base (e.g. TypeDef) instances - // However, it may invalidate iterators and references to namespace_members, because those are stored in std::vector - template - void add_database(std::string_view const& file, TypeFilter filter) - { - auto& db = m_databases.emplace_back(file, this); - for (auto&& type : db.TypeDef) - { - if (type.Flags().value == 0 || is_nested(type) || !filter(type)) - { - continue; - } - - auto& ns = m_namespaces[type.TypeNamespace()]; - auto[iter, inserted] = ns.types.try_emplace(type.TypeName(), type); - if (inserted) - { - add_type_to_members(type, ns); - } - } - - for (auto&& row : db.NestedClass) - { - m_nested_types[row.EnclosingType()].push_back(row.NestedType()); - } - } - - void add_database(std::string_view const& file) - { - add_database(file, default_type_filter{}); - } - - 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; - std::vector interfaces; - std::vector classes; - std::vector enums; - std::vector structs; - std::vector delegates; - std::vector attributes; - std::vector contracts; - }; - - using namespace_type = std::pair const&; - - private: - - void add_type_to_members(TypeDef const& type, namespace_members& members) - { - switch (get_category(type)) - { - case category::interface_type: - members.interfaces.push_back(type); - return; - case category::class_type: - if (extends_type(type, "System"sv, "Attribute"sv)) - { - members.attributes.push_back(type); - return; - } - members.classes.push_back(type); - return; - case category::enum_type: - members.enums.push_back(type); - return; - case category::struct_type: - if (get_attribute(type, "Windows.Foundation.Metadata"sv, "ApiContractAttribute"sv)) - { - members.contracts.push_back(type); - return; - } - members.structs.push_back(type); - return; - case category::delegate_type: - members.delegates.push_back(type); - return; - } - } - - std::list m_databases; - std::map m_namespaces; - std::map> m_nested_types; - }; -} \ No newline at end of file diff --git a/natvis/winmd/impl/winmd_reader/column.h b/natvis/winmd/impl/winmd_reader/column.h deleted file mode 100644 index 4ab754aac..000000000 --- a/natvis/winmd/impl/winmd_reader/column.h +++ /dev/null @@ -1,551 +0,0 @@ - -namespace winmd::reader -{ - template - template - auto row_base::get_list(uint32_t const column) const - { - auto const& my_table = get_database().template get_table(); - auto const& target_table = get_database().template get_table(); - - auto first = target_table.begin() + get_value(column) - 1; - auto last = target_table.end(); - if (index() + 1 < my_table.size()) - { - last = target_table.begin() + my_table[index() + 1].template get_value(column) - 1; - } - return std::pair{ first, last }; - } - - template - template - auto row_base::get_target_row(uint32_t const column) const - { - return get_database().template get_table()[get_value(column) - 1]; - } - - template - template - auto row_base::get_parent_row() const - { - struct compare - { - bool operator()(T const& lhs, uint32_t rhs) const noexcept - { - return lhs.template get_value(ParentColumn) < rhs; - } - bool operator()(uint32_t lhs, T const& rhs) const noexcept - { - return lhs < rhs.template get_value(ParentColumn); - } - }; - auto const& map = get_database().template get_table(); - return std::upper_bound(map.begin(), map.end(), index() + 1, compare{}) - 1; - } - - inline auto TypeDef::GenericParam() const - { - return equal_range(get_database().GenericParam, coded_index()); - } - - inline auto MethodDef::GenericParam() const - { - return equal_range(get_database().GenericParam, coded_index()); - } - - inline auto TypeDef::InterfaceImpl() const - { - struct compare - { - bool operator()(uint32_t const left, reader::InterfaceImpl const& right) noexcept - { - return left < right.get_value(0); - } - - bool operator()(reader::InterfaceImpl const& left, uint32_t const right) noexcept - { - return left.get_value(0) < right; - } - }; - - return equal_range(get_database().InterfaceImpl, index() + 1, compare{}); - } - - inline auto TypeDef::FieldList() const - { - return get_list(4); - } - - inline auto TypeDef::MethodList() const - { - return get_list(5); - } - - inline auto MethodDef::ParamList() const - { - return get_list(5); - } - - inline auto MethodDef::Parent() const - { - return get_parent_row(); - } - - inline auto Field::Parent() const - { - return get_parent_row(); - } - - inline auto InterfaceImpl::Class() const - { - return get_target_row(0); - } - - inline auto MethodSemantics::Method() const - { - return get_target_row(1); - } - - inline auto PropertyMap::Parent() const - { - return get_target_row(0); - } - - inline auto Property::MethodSemantic() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto Property::Parent() const - { - return get_parent_row().Parent(); - } - - inline auto PropertyMap::PropertyList() const - { - return get_list(1); - } - - inline auto EventMap::Parent() const - { - return get_target_row(0); - } - - inline auto EventMap::EventList() const - { - return get_list(1); - } - - inline auto Event::MethodSemantic() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto Event::Parent() const - { - return get_parent_row().Parent(); - } - - inline auto TypeDef::PropertyList() const - { - auto const& map = get_database().get_table(); - auto index = this->index() + 1; - auto iter = std::find_if(map.begin(), map.end(), [index](PropertyMap const& elem) - { - return elem.get_value(0) == index; - }); - if (iter == map.end()) - { - auto const& props = get_database().get_table(); - return std::pair{ props.end(), props.end() }; - } - else - { - return iter.PropertyList(); - } - } - - inline auto TypeDef::EventList() const - { - auto const& map = get_database().get_table(); - auto index = this->index() + 1; - auto iter = std::find_if(map.begin(), map.end(), [index](EventMap const& elem) - { - return elem.get_value(0) == index; - }); - if (iter == map.end()) - { - auto const& props = get_database().get_table(); - return std::pair{ props.end(), props.end() }; - } - else - { - return iter.EventList(); - } - } - - inline auto TypeDef::MethodImplList() const - { - struct compare - { - bool operator()(MethodImpl const& lhs, uint32_t rhs) const noexcept - { - return lhs.get_value(1) < rhs; - } - bool operator()(uint32_t lhs, MethodImpl const& rhs) const noexcept - { - return lhs < rhs.get_value(1); - } - }; - return equal_range(get_database().get_table(), index() + 1, compare{}); - } - - inline auto Field::Constant() const - { - auto const range = equal_range(get_database().Constant, coded_index()); - reader::Constant result; - if (range.second != range.first) - { - XLANG_ASSERT(range.second - range.first == 1); - result = range.first; - } - return result; - } - - inline auto Param::Constant() const - { - auto const range = equal_range(get_database().Constant, coded_index()); - reader::Constant result; - if (range.second != range.first) - { - XLANG_ASSERT(range.second - range.first == 1); - result = range.first; - } - return result; - } - - inline auto Property::Constant() const - { - auto const range = equal_range(get_database().Constant, coded_index()); - reader::Constant result; - if (range.second != range.first) - { - XLANG_ASSERT(range.second - range.first == 1); - result = range.first; - } - return result; - } - - inline auto MethodImpl::Class() const - { - return get_target_row(0); - } - - inline auto Constant::ValueBoolean() const - { - XLANG_ASSERT(Type() == ConstantType::Boolean); - return get_blob(2).as(); - } - - inline auto Constant::ValueChar() const - { - XLANG_ASSERT(Type() == ConstantType::Char); - return get_blob(2).as(); - } - - inline auto Constant::ValueInt8() const - { - XLANG_ASSERT(Type() == ConstantType::Int8); - return get_blob(2).as(); - } - - inline auto Constant::ValueUInt8() const - { - XLANG_ASSERT(Type() == ConstantType::UInt8); - return get_blob(2).as(); - } - - inline auto Constant::ValueInt16() const - { - XLANG_ASSERT(Type() == ConstantType::Int16); - return get_blob(2).as(); - } - - inline auto Constant::ValueUInt16() const - { - XLANG_ASSERT(Type() == ConstantType::UInt16); - return get_blob(2).as(); - } - - inline auto Constant::ValueInt32() const - { - XLANG_ASSERT(Type() == ConstantType::Int32); - return get_blob(2).as(); - } - - inline auto Constant::ValueUInt32() const - { - XLANG_ASSERT(Type() == ConstantType::UInt32); - return get_blob(2).as(); - } - - inline auto Constant::ValueInt64() const - { - XLANG_ASSERT(Type() == ConstantType::Int64); - return get_blob(2).as(); - } - - inline auto Constant::ValueUInt64() const - { - XLANG_ASSERT(Type() == ConstantType::UInt64); - return get_blob(2).as(); - } - - inline auto Constant::ValueFloat32() const - { - XLANG_ASSERT(Type() == ConstantType::Float32); - return get_blob(2).as(); - } - - inline auto Constant::ValueFloat64() const - { - XLANG_ASSERT(Type() == ConstantType::Float64); - return get_blob(2).as(); - } - - inline auto Constant::ValueString() const - { - XLANG_ASSERT(Type() == ConstantType::String); - return get_blob(2).as_u16string_constant(); - } - - inline auto Constant::ValueClass() const - { - XLANG_ASSERT(Type() == ConstantType::Class); - XLANG_ASSERT(get_blob(2).as() == 0); - return nullptr; - } - - inline Constant::constant_type Constant::Value() const - { - switch (Type()) - { - case ConstantType::Boolean: - return ValueBoolean(); - case ConstantType::Char: - return ValueChar(); - case ConstantType::Int8: - return ValueInt8(); - case ConstantType::UInt8: - return ValueUInt8(); - case ConstantType::Int16: - return ValueInt16(); - case ConstantType::UInt16: - return ValueUInt16(); - case ConstantType::Int32: - return ValueInt32(); - case ConstantType::UInt32: - return ValueUInt32(); - case ConstantType::Int64: - return ValueInt64(); - case ConstantType::UInt64: - return ValueUInt64(); - case ConstantType::Float32: - return ValueFloat32(); - case ConstantType::Float64: - return ValueFloat64(); - case ConstantType::String: - return ValueString(); - case ConstantType::Class: - return ValueClass(); - default: - impl::throw_invalid("Invalid constant type"); - } - } - - inline auto MethodDef::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto Field::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto TypeRef::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto TypeDef::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto Param::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto InterfaceImpl::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto MemberRef::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto Module::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto Property::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto Event::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto StandAloneSig::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto ModuleRef::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto TypeSpec::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto Assembly::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto AssemblyRef::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto File::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto ExportedType::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto ManifestResource::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto GenericParam::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto GenericParamConstraint::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - inline auto MethodSpec::CustomAttribute() const - { - return equal_range(get_database().get_table(), coded_index()); - } - - struct AssemblyVersion - { - uint16_t MajorVersion; - uint16_t MinorVersion; - uint16_t BuildNumber; - uint16_t RevisionNumber; - }; - - inline auto Assembly::Version() const - { - auto const temp = get_value(1); - return AssemblyVersion{ static_cast(temp & 0xffff), static_cast((temp >> 16) & 0xffff), static_cast((temp >> 32) & 0xffff), static_cast((temp >> 48) & 0xffff) }; - } - - inline auto AssemblyRef::Version() const - { - auto const temp = get_value(0); - return AssemblyVersion{ static_cast(temp & 0xffff), static_cast((temp >> 16) & 0xffff), static_cast((temp >> 32) & 0xffff), static_cast((temp >> 48) & 0xffff) }; - } - - inline auto AssemblyRefOS::AssemblyRef() const - { - return get_target_row(3); - } - - inline auto AssemblyRefProcessor::AssemblyRef() const - { - return get_target_row(3); - } - - inline auto ClassLayout::Parent() const - { - 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/natvis/winmd/impl/winmd_reader/custom_attribute.h b/natvis/winmd/impl/winmd_reader/custom_attribute.h deleted file mode 100644 index 36b56a949..000000000 --- a/natvis/winmd/impl/winmd_reader/custom_attribute.h +++ /dev/null @@ -1,385 +0,0 @@ - -namespace winmd::reader -{ - inline auto CustomAttribute::TypeNamespaceAndName() const - { - if (Type().type() == CustomAttributeType::MemberRef) - { - auto const& member_parent = Type().MemberRef().Class(); - switch (member_parent.type()) - { - case MemberRefParent::TypeDef: - { - auto const& def = member_parent.TypeDef(); - return std::pair{ def.TypeNamespace(), def.TypeName() }; - } - - case MemberRefParent::TypeRef: - { - auto const& ref = member_parent.TypeRef(); - return std::pair{ ref.TypeNamespace(), ref.TypeName() }; - } - default: - impl::throw_invalid("A CustomAttribute MemberRef should only be a TypeDef or TypeRef"); - } - } - else - { - auto const& def = Type().MethodDef().Parent(); - return std::pair{ def.TypeNamespace(), def.TypeName() }; - } - } - - struct ElemSig - { - struct SystemType - { - std::string_view name; - }; - - struct EnumValue - { - EnumDefinition type; - using value_type = std::variant; - value_type value; - - bool equals_enumerator(std::string_view const& name) const - { - auto field = type.get_enumerator(name); - auto constant_value = std::visit([](auto&& v) { return Constant::constant_type{ v }; }, value); - return field.Constant().Value() == constant_value; - } - }; - - using value_type = std::variant; - - ElemSig(database const& db, ParamSig const& param, byte_view& data) - : value{ read_element(db, param, data) } - { - } - - ElemSig(SystemType type) - : value(type) - { - } - - ElemSig(EnumDefinition const& enum_def, byte_view& data) - : value{ EnumValue{enum_def, read_enum(enum_def.m_underlying_type, data) } } - { - } - - ElemSig(ElementType type, byte_view& data) - : value{ read_primitive(type, data) } - { - } - - static value_type read_element(database const& db, ParamSig const& param, byte_view& data) - { - auto const& type = param.Type().Type(); - if (auto element_type = std::get_if(&type)) - { - return read_primitive(*element_type, data); - } - else if (auto type_index = std::get_if>(&type)) - { - if ((type_index->type() == TypeDefOrRef::TypeRef && type_index->TypeRef().TypeNamespace() == "System" && type_index->TypeRef().TypeName() == "Type") || - (type_index->type() == TypeDefOrRef::TypeDef && type_index->TypeDef().TypeNamespace() == "System" && type_index->TypeDef().TypeName() == "Type")) - { - return SystemType{ read(data) }; - } - else - { - // Should be an enum. Resolve it. - auto resolve_type = [&db, &type_index]() -> TypeDef - { - if (type_index->type() == TypeDefOrRef::TypeDef) - { - return type_index->TypeDef(); - } - auto const& typeref = type_index->TypeRef(); - return db.get_cache().find_required(typeref.TypeNamespace(), typeref.TypeName()); - }; - TypeDef const& enum_type = resolve_type(); - if (!enum_type.is_enum()) - { - impl::throw_invalid("CustomAttribute params that are TypeDefOrRef must be an enum or System.Type"); - } - - auto const& enum_def = enum_type.get_enum_definition(); - return EnumValue{ enum_def, read_enum(enum_def.m_underlying_type, data) }; - } - } - impl::throw_invalid("Custom attribute params must be primitives, enums, or System.Type"); - } - - static value_type read_primitive(ElementType type, byte_view& data) - { - switch (type) - { - case ElementType::Boolean: - return read(data); - - case ElementType::Char: - return read(data); - - case ElementType::I1: - return read(data); - - case ElementType::U1: - return read(data); - - case ElementType::I2: - return read(data); - - case ElementType::U2: - return read(data); - - case ElementType::I4: - return read(data); - - case ElementType::U4: - return read(data); - - case ElementType::I8: - return read(data); - - case ElementType::U8: - return read(data); - - case ElementType::R4: - return read(data); - - case ElementType::R8: - return read(data); - - case ElementType::String: - return read(data); - - default: - impl::throw_invalid("Non-primitive type encountered"); - } - } - - static EnumValue::value_type read_enum(ElementType type, byte_view& data) - { - switch (type) - { - case ElementType::Boolean: - return read(data); - - case ElementType::Char: - return read(data); - - case ElementType::I1: - return read(data); - - case ElementType::U1: - return read(data); - - case ElementType::I2: - return read(data); - - case ElementType::U2: - return read(data); - - case ElementType::I4: - return read(data); - - case ElementType::U4: - return read(data); - - case ElementType::I8: - return read(data); - - case ElementType::U8: - return read(data); - - default: - impl::throw_invalid("Invalid underling enum type encountered"); - } - } - - value_type value; - }; - - struct FixedArgSig - { - using value_type = std::variant>; - - FixedArgSig(database const& db, ParamSig const& ctor_param, byte_view& data) - : value{ read_arg(db, ctor_param, data) } - {} - - FixedArgSig(ElemSig::SystemType type) - : value{ ElemSig{type} } - {} - - FixedArgSig(EnumDefinition const& enum_def, byte_view& data) - : value{ ElemSig{ enum_def, data } } - {} - - FixedArgSig(ElementType type, bool is_array, byte_view& data) - : value{ read_arg(type, is_array, data) } - {} - - static value_type read_arg(database const& db, ParamSig const& ctor_param, byte_view& data) - { - auto const& type_sig = ctor_param.Type(); - if (type_sig.is_szarray()) - { - std::vector elems; - auto const num_elements = read(data); - if (num_elements != 0xffffffff) - { - if (num_elements > data.size()) - { - impl::throw_invalid("Invalid blob array size"); - } - elems.reserve(num_elements); - for (uint32_t i = 0; i < num_elements; ++i) - { - elems.emplace_back(db, ctor_param, data); - } - } - return elems; - } - else - { - return ElemSig{ db, ctor_param, data }; - } - } - - static value_type read_arg(ElementType type, bool is_array, byte_view& data) - { - if (is_array) - { - std::vector elems; - auto const num_elements = read(data); - if (num_elements != 0xffffffff) - { - if (num_elements > data.size()) - { - impl::throw_invalid("Invalid blob array size"); - } - elems.reserve(num_elements); - for (uint32_t i = 0; i < num_elements; ++i) - { - elems.emplace_back(type, data); - } - } - return elems; - } - else - { - return ElemSig{ type, data }; - } - } - - value_type value; - }; - - struct NamedArgSig - { - NamedArgSig(database const& db, byte_view& data) - : value{ parse_value(db, data) } - {} - - std::string_view name; - FixedArgSig value; - - private: - FixedArgSig parse_value(database const& db, byte_view& data) - { - auto const field_or_prop = read(data); - if (field_or_prop != ElementType::Field && field_or_prop != ElementType::Property) - { - impl::throw_invalid("NamedArg must be either FIELD or PROPERTY"); - } - - auto type = read(data); - switch (type) - { - case ElementType::Type: - name = read(data); - return FixedArgSig{ ElemSig::SystemType{read(data)} }; - - case ElementType::Enum: - { - auto type_string = read(data); - name = read(data); - auto type_def = db.get_cache().find(type_string); - if (!type_def) - { - impl::throw_invalid("CustomAttribute named param referenced unresolved enum type"); - } - if (!type_def.is_enum()) - { - impl::throw_invalid("CustomAttribute named param referenced non-enum type"); - } - - return FixedArgSig{ type_def.get_enum_definition(), data }; - } - - default: - { - bool const is_array = (type == ElementType::SZArray); - if (is_array) - { - type = read(data); - } - if (type < ElementType::Boolean || ElementType::String < type) - { - impl::throw_invalid("CustomAttribute named param must be a primitive, System.Type, or an Enum"); - } - name = read(data); - return FixedArgSig{ type, is_array, data }; - } - } - } - }; - - struct CustomAttributeSig - { - CustomAttributeSig(table_base const* table, byte_view& data, MethodDefSig const& ctor) - { - database const& db = table->get_database(); - auto const prolog = read(data); - if (prolog != 0x0001) - { - impl::throw_invalid("CustomAttribute blobs must start with prolog of 0x0001"); - } - - for (auto const& param : ctor.Params()) - { - m_fixed_args.push_back(FixedArgSig{ db, param, data }); - } - - const auto num_named_args = read(data); - if (num_named_args > data.size()) - { - impl::throw_invalid("Invalid blob array size"); - } - m_named_args.reserve(num_named_args); - - for (uint16_t i = 0; i < num_named_args; ++i) - { - m_named_args.emplace_back(db, data); - } - } - - std::vector const& FixedArgs() const noexcept { return m_fixed_args; } - std::vector const& NamedArgs() const noexcept { return m_named_args; } - - private: - std::vector m_fixed_args; - std::vector m_named_args; - }; - - inline auto CustomAttribute::Value() const - { - auto const ctor = Type(); - MethodDefSig const& method_sig = ctor.type() == CustomAttributeType::MemberRef ? ctor.MemberRef().MethodSignature() : ctor.MethodDef().Signature(); - auto cursor = get_blob(2); - return CustomAttributeSig{ get_table(), cursor, method_sig }; - } -} diff --git a/natvis/winmd/impl/winmd_reader/database.h b/natvis/winmd/impl/winmd_reader/database.h deleted file mode 100644 index 9a6b50542..000000000 --- a/natvis/winmd/impl/winmd_reader/database.h +++ /dev/null @@ -1,624 +0,0 @@ - -namespace winmd::impl -{ - constexpr uint8_t bits_needed(uint32_t value) noexcept - { - --value; - uint8_t bits{ 1 }; - - while (value >>= 1) - { - ++bits; - } - - return bits; - } - - static_assert(bits_needed(2) == 1); - static_assert(bits_needed(3) == 2); - static_assert(bits_needed(4) == 2); - static_assert(bits_needed(5) == 3); - static_assert(bits_needed(22) == 5); -} - -namespace winmd::reader -{ - struct cache; - - struct database - { - database(database&&) = delete; - database& operator=(database&&) = delete; - - static bool is_database(std::string_view const& path) - { - file_view file{ path }; - - if (file.size() < sizeof(impl::image_dos_header)) - { - return false; - } - - auto dos = file.as(); - - if (dos.e_signature != 0x5A4D) // IMAGE_DOS_SIGNATURE - { - return false; - } - - if (file.size() < (dos.e_lfanew + sizeof(impl::image_nt_headers32))) - { - return false; - } - - auto pe = file.as(dos.e_lfanew); - - if (pe.FileHeader.NumberOfSections == 0 || pe.FileHeader.NumberOfSections > 100) - { - return false; - } - - impl::image_section_header const* sections{}; - uint32_t com_virtual_address{}; - if (pe.OptionalHeader.Magic == 0x10B) // PE32 - { - com_virtual_address = pe.OptionalHeader.DataDirectory[14].VirtualAddress; // IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR - sections = &file.as(dos.e_lfanew + sizeof(impl::image_nt_headers32)); - } - else if (pe.OptionalHeader.Magic == 0x20B) // PE32+ - { - auto pe_plus = file.as(dos.e_lfanew); - com_virtual_address = pe_plus.OptionalHeader.DataDirectory[14].VirtualAddress; // IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR - sections = &file.as(dos.e_lfanew + sizeof(impl::image_nt_headers32plus)); - } - else - { - impl::throw_invalid("Invalid optional header magic value"); - } - auto sections_end = sections + pe.FileHeader.NumberOfSections; - auto section = section_from_rva(sections, sections_end, com_virtual_address); - - if (section == sections_end) - { - return false; - } - - auto offset = offset_from_rva(*section, com_virtual_address); - - auto cli = file.as(offset); - - if (cli.cb != sizeof(impl::image_cor20_header)) - { - return false; - } - - section = section_from_rva(sections, sections_end, cli.MetaData.VirtualAddress); - - if (section == sections_end) - { - return false; - } - - offset = offset_from_rva(*section, cli.MetaData.VirtualAddress); - - if (file.as(offset) != 0x424a5342) - { - return false; - } - - return true; - } - - explicit database(std::vector&& buffer, cache const* cache = nullptr) : m_buffer{ std::move(buffer) }, m_view{ m_buffer.data(), m_buffer.data() + m_buffer.size() }, m_cache{ cache } - { - initialize(); - } - - explicit database(std::string_view const& path, cache const* cache = nullptr) : m_view{ path }, m_path{ path }, m_cache{ cache } - { - initialize(); - } - - table TypeRef{ this }; - table GenericParamConstraint{ this }; - table TypeSpec{ this }; - table TypeDef{ this }; - table CustomAttribute{ this }; - table MethodDef{ this }; - table MemberRef{ this }; - table Module{ this }; - table Param{ this }; - table InterfaceImpl{ this }; - table Constant{ this }; - table Field{ this }; - table FieldMarshal{ this }; - table DeclSecurity{ this }; - table ClassLayout{ this }; - table FieldLayout{ this }; - table StandAloneSig{ this }; - table EventMap{ this }; - table Event{ this }; - table PropertyMap{ this }; - table Property{ this }; - table MethodSemantics{ this }; - table MethodImpl{ this }; - table ModuleRef{ this }; - table ImplMap{ this }; - table FieldRVA{ this }; - table Assembly{ this }; - table AssemblyProcessor{ this }; - table AssemblyOS{ this }; - table AssemblyRef{ this }; - table AssemblyRefProcessor{ this }; - table AssemblyRefOS{ this }; - table File{ this }; - table ExportedType{ this }; - table ManifestResource{ this }; - table NestedClass{ this }; - table GenericParam{ this }; - table MethodSpec{ this }; - - template - table const& get_table() const noexcept; - - cache const& get_cache() const noexcept - { - return *m_cache; - } - - std::string const& path() const noexcept - { - return m_path; - } - - std::string_view get_string(uint32_t const index) const - { - auto view = m_strings.seek(index); - auto last = std::find(view.begin(), view.end(), 0); - - if (last == view.end()) - { - impl::throw_invalid("Missing string terminator"); - } - - return { reinterpret_cast(view.begin()), static_cast(last - view.begin()) }; - } - - byte_view get_blob(uint32_t const index) const - { - auto view = m_blobs.seek(index); - auto initial_byte = view.as(); - uint32_t blob_size_bytes{}; - - switch (initial_byte >> 5) - { - case 0: - case 1: - case 2: - case 3: - blob_size_bytes = 1; - initial_byte &= 0x7f; - break; - - case 4: - case 5: - blob_size_bytes = 2; - initial_byte &= 0x3f; - break; - - case 6: - blob_size_bytes = 4; - initial_byte &= 0x1f; - break; - - default: - impl::throw_invalid("Invalid blob encoding"); - } - - uint32_t blob_size{ initial_byte }; - - for (auto&& byte : view.sub(1, blob_size_bytes - 1)) - { - blob_size = (blob_size << 8) + byte; - } - - return { view.sub(blob_size_bytes, blob_size) }; - } - - private: - void initialize() - { - auto dos = m_view.as(); - - if (dos.e_signature != 0x5A4D) // IMAGE_DOS_SIGNATURE - { - impl::throw_invalid("Invalid DOS signature"); - } - - auto pe = m_view.as(dos.e_lfanew); - - if (pe.FileHeader.NumberOfSections == 0 || pe.FileHeader.NumberOfSections > 100) - { - impl::throw_invalid("Invalid PE section count"); - } - - impl::image_section_header const* sections{}; - uint32_t com_virtual_address{}; - if (pe.OptionalHeader.Magic == 0x10B) // PE32 - { - com_virtual_address = pe.OptionalHeader.DataDirectory[14].VirtualAddress; // IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR - sections = &m_view.as(dos.e_lfanew + sizeof(impl::image_nt_headers32)); - } - else if (pe.OptionalHeader.Magic == 0x20B) // PE32+ - { - auto pe_plus = m_view.as(dos.e_lfanew); - com_virtual_address = pe_plus.OptionalHeader.DataDirectory[14].VirtualAddress; // IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR - sections = &m_view.as(dos.e_lfanew + sizeof(impl::image_nt_headers32plus)); - } - else - { - impl::throw_invalid("Invalid optional header magic value"); - } - auto sections_end = sections + pe.FileHeader.NumberOfSections; - auto section = section_from_rva(sections, sections_end, com_virtual_address); - - if (section == sections_end) - { - impl::throw_invalid("PE section containing CLI header not found"); - } - - auto offset = offset_from_rva(*section, com_virtual_address); - - auto cli = m_view.as(offset); - - if (cli.cb != sizeof(impl::image_cor20_header)) - { - impl::throw_invalid("Invalid CLI header"); - } - - section = section_from_rva(sections, sections_end, cli.MetaData.VirtualAddress); - - if (section == sections_end) - { - impl::throw_invalid("PE section containing CLI metadata not found"); - } - - offset = offset_from_rva(*section, cli.MetaData.VirtualAddress); - - if (m_view.as(offset) != 0x424a5342) - { - impl::throw_invalid("CLI metadata magic signature not found"); - } - - auto version_length = m_view.as(offset + 12); - auto stream_count = m_view.as(offset + version_length + 18); - auto view = m_view.seek(offset + version_length + 20); - byte_view tables; - - for (uint16_t i{}; i < stream_count; ++i) - { - auto stream = view.as(); - auto name = view.as>(8); - - if (name.data() == "#Strings"sv) - { - m_strings = m_view.sub(offset + stream.offset, stream.size); - } - else if (name.data() == "#Blob"sv) - { - m_blobs = m_view.sub(offset + stream.offset, stream.size); - } - else if (name.data() == "#GUID"sv) - { - m_guids = m_view.sub(offset + stream.offset, stream.size); - } - else if (name.data() == "#~"sv) - { - tables = m_view.sub(offset + stream.offset, stream.size); - } - else if (name.data() != "#US"sv) - { - impl::throw_invalid("Unknown metadata stream"); - } - - view = view.seek(stream_offset(name.data())); - } - - std::bitset<8> const heap_sizes{ tables.as(6) }; - uint8_t const string_index_size = heap_sizes.test(0) ? 4 : 2; - uint8_t const guid_index_size = heap_sizes.test(1) ? 4 : 2; - uint8_t const blob_index_size = heap_sizes.test(2) ? 4 : 2; - - std::bitset<64> const valid_bits{ tables.as(8) }; - view = tables.seek(24); - - for (uint32_t i{}; i < 64; ++i) - { - if (!valid_bits.test(i)) - { - continue; - } - - auto row_count = view.as(); - view = view.seek(4); - - switch (i) - { - case 0x00: Module.set_row_count(row_count); break; - case 0x01: TypeRef.set_row_count(row_count); break; - case 0x02: TypeDef.set_row_count(row_count); break; - case 0x04: Field.set_row_count(row_count); break; - case 0x06: MethodDef.set_row_count(row_count); break; - case 0x08: Param.set_row_count(row_count); break; - case 0x09: InterfaceImpl.set_row_count(row_count); break; - case 0x0a: MemberRef.set_row_count(row_count); break; - case 0x0b: Constant.set_row_count(row_count); break; - case 0x0c: CustomAttribute.set_row_count(row_count); break; - case 0x0d: FieldMarshal.set_row_count(row_count); break; - case 0x0e: DeclSecurity.set_row_count(row_count); break; - case 0x0f: ClassLayout.set_row_count(row_count); break; - case 0x10: FieldLayout.set_row_count(row_count); break; - case 0x11: StandAloneSig.set_row_count(row_count); break; - case 0x12: EventMap.set_row_count(row_count); break; - case 0x14: Event.set_row_count(row_count); break; - case 0x15: PropertyMap.set_row_count(row_count); break; - case 0x17: Property.set_row_count(row_count); break; - case 0x18: MethodSemantics.set_row_count(row_count); break; - case 0x19: MethodImpl.set_row_count(row_count); break; - case 0x1a: ModuleRef.set_row_count(row_count); break; - case 0x1b: TypeSpec.set_row_count(row_count); break; - case 0x1c: ImplMap.set_row_count(row_count); break; - case 0x1d: FieldRVA.set_row_count(row_count); break; - case 0x20: Assembly.set_row_count(row_count); break; - case 0x21: AssemblyProcessor.set_row_count(row_count); break; - case 0x22: AssemblyOS.set_row_count(row_count); break; - case 0x23: AssemblyRef.set_row_count(row_count); break; - case 0x24: AssemblyRefProcessor.set_row_count(row_count); break; - case 0x25: AssemblyRefOS.set_row_count(row_count); break; - case 0x26: File.set_row_count(row_count); break; - case 0x27: ExportedType.set_row_count(row_count); break; - case 0x28: ManifestResource.set_row_count(row_count); break; - case 0x29: NestedClass.set_row_count(row_count); break; - case 0x2a: GenericParam.set_row_count(row_count); break; - case 0x2b: MethodSpec.set_row_count(row_count); break; - case 0x2c: GenericParamConstraint.set_row_count(row_count); break; - default: impl::throw_invalid("Unknown metadata table"); - }; - } - - table_base const empty_table{ nullptr }; - - auto const TypeDefOrRef = composite_index_size(TypeDef, TypeRef, TypeSpec); - auto const HasConstant = composite_index_size(Field, Param, Property); - auto const HasCustomAttribute = composite_index_size(MethodDef, Field, TypeRef, TypeDef, Param, InterfaceImpl, MemberRef, Module, Property, Event, StandAloneSig, ModuleRef, TypeSpec, Assembly, AssemblyRef, File, ExportedType, ManifestResource, GenericParam, GenericParamConstraint, MethodSpec); - auto const HasFieldMarshal = composite_index_size(Field, Param); - auto const HasDeclSecurity = composite_index_size(TypeDef, MethodDef, Assembly); - auto const MemberRefParent = composite_index_size(TypeDef, TypeRef, ModuleRef, MethodDef, TypeSpec); - auto const HasSemantics = composite_index_size(Event, Property); - auto const MethodDefOrRef = composite_index_size(MethodDef, MemberRef); - auto const MemberForwarded = composite_index_size(Field, MethodDef); - auto const Implementation = composite_index_size(File, AssemblyRef, ExportedType); - auto const CustomAttributeType = composite_index_size(MethodDef, MemberRef, empty_table, empty_table, empty_table); - auto const ResolutionScope = composite_index_size(Module, ModuleRef, AssemblyRef, TypeRef); - auto const TypeOrMethodDef = composite_index_size(TypeDef, MethodDef); - - Assembly.set_columns(4, 8, 4, blob_index_size, string_index_size, string_index_size); - AssemblyOS.set_columns(4, 4, 4); - AssemblyProcessor.set_columns(4); - AssemblyRef.set_columns(8, 4, blob_index_size, string_index_size, string_index_size, blob_index_size); - AssemblyRefOS.set_columns(4, 4, 4, AssemblyRef.index_size()); - AssemblyRefProcessor.set_columns(4, AssemblyRef.index_size()); - ClassLayout.set_columns(2, 4, TypeDef.index_size()); - Constant.set_columns(2, HasConstant, blob_index_size); - CustomAttribute.set_columns(HasCustomAttribute, CustomAttributeType, blob_index_size); - DeclSecurity.set_columns(2, HasDeclSecurity, blob_index_size); - EventMap.set_columns(TypeDef.index_size(), Event.index_size()); - Event.set_columns(2, string_index_size, TypeDefOrRef); - ExportedType.set_columns(4, 4, string_index_size, string_index_size, Implementation); - Field.set_columns(2, string_index_size, blob_index_size); - FieldLayout.set_columns(4, Field.index_size()); - FieldMarshal.set_columns(HasFieldMarshal, blob_index_size); - FieldRVA.set_columns(4, Field.index_size()); - File.set_columns(4, string_index_size, blob_index_size); - GenericParam.set_columns(2, 2, TypeOrMethodDef, string_index_size); - GenericParamConstraint.set_columns(GenericParam.index_size(), TypeDefOrRef); - ImplMap.set_columns(2, MemberForwarded, string_index_size, ModuleRef.index_size()); - InterfaceImpl.set_columns(TypeDef.index_size(), TypeDefOrRef); - ManifestResource.set_columns(4, 4, string_index_size, Implementation); - MemberRef.set_columns(MemberRefParent, string_index_size, blob_index_size); - MethodDef.set_columns(4, 2, 2, string_index_size, blob_index_size, Param.index_size()); - MethodImpl.set_columns(TypeDef.index_size(), MethodDefOrRef, MethodDefOrRef); - MethodSemantics.set_columns(2, MethodDef.index_size(), HasSemantics); - MethodSpec.set_columns(MethodDefOrRef, blob_index_size); - Module.set_columns(2, string_index_size, guid_index_size, guid_index_size, guid_index_size); - ModuleRef.set_columns(string_index_size); - NestedClass.set_columns(TypeDef.index_size(), TypeDef.index_size()); - Param.set_columns(2, 2, string_index_size); - Property.set_columns(2, string_index_size, blob_index_size); - PropertyMap.set_columns(TypeDef.index_size(), Property.index_size()); - StandAloneSig.set_columns(blob_index_size); - TypeDef.set_columns(4, string_index_size, string_index_size, TypeDefOrRef, Field.index_size(), MethodDef.index_size()); - TypeRef.set_columns(ResolutionScope, string_index_size, string_index_size); - TypeSpec.set_columns(blob_index_size); - - Module.set_data(view); - TypeRef.set_data(view); - TypeDef.set_data(view); - Field.set_data(view); - MethodDef.set_data(view); - Param.set_data(view); - InterfaceImpl.set_data(view); - MemberRef.set_data(view); - Constant.set_data(view); - CustomAttribute.set_data(view); - FieldMarshal.set_data(view); - DeclSecurity.set_data(view); - ClassLayout.set_data(view); - FieldLayout.set_data(view); - StandAloneSig.set_data(view); - EventMap.set_data(view); - Event.set_data(view); - PropertyMap.set_data(view); - Property.set_data(view); - MethodSemantics.set_data(view); - MethodImpl.set_data(view); - ModuleRef.set_data(view); - TypeSpec.set_data(view); - ImplMap.set_data(view); - FieldRVA.set_data(view); - Assembly.set_data(view); - AssemblyProcessor.set_data(view); - AssemblyOS.set_data(view); - AssemblyRef.set_data(view); - AssemblyRefProcessor.set_data(view); - AssemblyRefOS.set_data(view); - File.set_data(view); - ExportedType.set_data(view); - ManifestResource.set_data(view); - NestedClass.set_data(view); - GenericParam.set_data(view); - MethodSpec.set_data(view); - GenericParamConstraint.set_data(view); - } - - struct stream_range - { - uint32_t offset; - uint32_t size; - }; - - static bool composite_index_size(uint32_t const row_count, uint8_t const bits) - { - return row_count < (1ull << (16 - bits)); - } - - template - static uint8_t composite_index_size(Tables const&... tables) - { - return (composite_index_size(tables.size(), impl::bits_needed(sizeof...(tables))) && ...) ? 2 : 4; - } - - static uint32_t stream_offset(std::string_view const& name) noexcept - { - uint32_t padding = 4 - name.size() % 4; - - if (padding == 0) - { - padding = 4; - } - - return static_cast(8 + name.size() + padding); - } - - static impl::image_section_header const* section_from_rva(impl::image_section_header const* const first, impl::image_section_header const* const last, uint32_t const rva) noexcept - { - return std::find_if(first, last, [rva](auto&& section) noexcept - { - return rva >= section.VirtualAddress && rva < section.VirtualAddress + section.Misc.VirtualSize; - }); - } - - static uint32_t offset_from_rva(impl::image_section_header const& section, uint32_t const rva) noexcept - { - return rva - section.VirtualAddress + section.PointerToRawData; - } - - std::vector m_buffer; - file_view m_view; - - std::string const m_path; - byte_view m_strings; - byte_view m_blobs; - byte_view m_guids; - cache const* m_cache; - }; - - template - inline byte_view row_base::get_blob(uint32_t const column) const - { - return get_database().get_blob(m_table->get_value(m_index, column)); - } - - template - inline std::string_view row_base::get_string(uint32_t const column) const - { - return get_database().get_string(m_table->get_value(m_index, column)); - } - - template <> - inline table const& database::get_table() const noexcept { return Module; } - template <> - inline table const& database::get_table() const noexcept { return TypeRef; } - template <> - inline table const& database::get_table() const noexcept { return TypeDef; } - template <> - inline table const& database::get_table() const noexcept { return Field; } - template <> - inline table const& database::get_table() const noexcept { return MethodDef; } - template <> - inline table const& database::get_table() const noexcept { return Param; } - template <> - inline table const& database::get_table() const noexcept { return InterfaceImpl; } - template <> - inline table const& database::get_table() const noexcept { return MemberRef; } - template <> - inline table const& database::get_table() const noexcept { return Constant; } - template <> - inline table const& database::get_table() const noexcept { return CustomAttribute; } - template <> - inline table const& database::get_table() const noexcept { return FieldMarshal; } - template <> - inline table const& database::get_table() const noexcept { return DeclSecurity; } - template <> - inline table const& database::get_table() const noexcept { return ClassLayout; } - template <> - inline table const& database::get_table() const noexcept { return FieldLayout; } - template <> - inline table const& database::get_table() const noexcept { return StandAloneSig; } - template <> - inline table const& database::get_table() const noexcept { return EventMap; } - template <> - inline table const& database::get_table() const noexcept { return Event; } - template <> - inline table const& database::get_table() const noexcept { return PropertyMap; } - template <> - inline table const& database::get_table() const noexcept { return Property; } - template <> - inline table const& database::get_table() const noexcept { return MethodSemantics; } - template <> - inline table const& database::get_table() const noexcept { return MethodImpl; } - template <> - inline table const& database::get_table() const noexcept { return ModuleRef; } - template <> - inline table const& database::get_table() const noexcept { return TypeSpec; } - template <> - inline table const& database::get_table() const noexcept { return ImplMap; } - template <> - inline table const& database::get_table() const noexcept { return FieldRVA; } - template <> - inline table const& database::get_table() const noexcept { return Assembly; } - template <> - inline table const& database::get_table() const noexcept { return AssemblyProcessor; } - template <> - inline table const& database::get_table() const noexcept { return AssemblyOS; } - template <> - inline table const& database::get_table() const noexcept { return AssemblyRef; } - template <> - inline table const& database::get_table() const noexcept { return AssemblyRefProcessor; } - template <> - inline table const& database::get_table() const noexcept { return AssemblyRefOS; } - template <> - inline table const& database::get_table() const noexcept { return File; } - template <> - inline table const& database::get_table() const noexcept { return ExportedType; } - template <> - inline table const& database::get_table() const noexcept { return ManifestResource; } - template <> - inline table const& database::get_table() const noexcept { return NestedClass; } - template <> - inline table const& database::get_table() const noexcept { return GenericParam; } - template <> - inline table const& database::get_table() const noexcept { return MethodSpec; } - template <> - inline table const& database::get_table() const noexcept { return GenericParamConstraint; } -} diff --git a/natvis/winmd/impl/winmd_reader/enum.h b/natvis/winmd/impl/winmd_reader/enum.h deleted file mode 100644 index 8792e3e78..000000000 --- a/natvis/winmd/impl/winmd_reader/enum.h +++ /dev/null @@ -1,329 +0,0 @@ - -namespace winmd::reader -{ - template - struct coded_index_bits : std::integral_constant {}; - - template - inline constexpr uint32_t coded_index_bits_v = coded_index_bits::value; - - enum class TypeDefOrRef : uint32_t - { - TypeDef, - TypeRef, - TypeSpec, - }; - template <> - struct coded_index_bits : std::integral_constant {}; - - enum class HasConstant : uint32_t - { - Field, - Param, - Property, - }; - template <> - struct coded_index_bits : std::integral_constant {}; - - enum class HasCustomAttribute : uint32_t - { - MethodDef, - Field, - TypeRef, - TypeDef, - Param, - InterfaceImpl, - MemberRef, - Module, - Permission, - Property, - Event, - StandAloneSig, - ModuleRef, - TypeSpec, - Assembly, - AssemblyRef, - File, - ExportedType, - ManifestResource, - GenericParam, - GenericParamConstraint, - MethodSpec, - }; - template <> - struct coded_index_bits : std::integral_constant {}; - - enum class HasFieldMarshal : uint32_t - { - Field, - Param, - }; - template <> - struct coded_index_bits : std::integral_constant {}; - - enum class HasDeclSecurity : uint32_t - { - TypeDef, - MethodDef, - Assembly, - }; - template <> - struct coded_index_bits : std::integral_constant {}; - - enum class MemberRefParent : uint32_t - { - TypeDef, - TypeRef, - ModuleRef, - MethodDef, - TypeSpec, - }; - template <> - struct coded_index_bits : std::integral_constant {}; - - enum class HasSemantics : uint32_t - { - Event, - Property, - }; - template <> - struct coded_index_bits : std::integral_constant {}; - - enum class MethodDefOrRef : uint32_t - { - MethodDef, - MemberRef, - }; - template <> - struct coded_index_bits : std::integral_constant {}; - - enum class MemberForwarded : uint32_t - { - Field, - MethodDef, - }; - template <> - struct coded_index_bits : std::integral_constant {}; - - enum class Implementation : uint32_t - { - File, - AssemblyRef, - ExportedType, - }; - template <> - struct coded_index_bits : std::integral_constant {}; - - enum class CustomAttributeType : uint32_t - { - MethodDef = 2, - MemberRef, - }; - template <> - struct coded_index_bits : std::integral_constant {}; - - enum class ResolutionScope : uint32_t - { - Module, - ModuleRef, - AssemblyRef, - TypeRef, - }; - template <> - struct coded_index_bits : std::integral_constant {}; - - enum class TypeOrMethodDef : uint32_t - { - TypeDef, - MethodDef, - }; - template <> - struct coded_index_bits : std::integral_constant {}; - - enum class MemberAccess : uint16_t - { - CompilerControlled = 0x0000, // Member not referenceable - Private = 0x0001, - FamAndAssem = 0x0002, // Accessible by subtypes only in this Assembly - Assembly = 0x0003, // Accessible by anyone in this Assembly - Family = 0x0004, // aka Protected - FamOrAssem = 0x0005, // Accessible by subtypes anywhere, plus anyone in this Assembly - Public = 0x0006, - }; - - enum class TypeVisibility : uint32_t - { - NotPublic = 0x00000000, - Public = 0x00000001, - NestedPublic = 0x00000002, - NestedPrivate = 0x00000003, - NestedFamily = 0x00000004, - NestedAssembly = 0x00000005, - NestedFamANDAssem = 0x00000006, - NestedFamORAssem = 0x00000007, - }; - - enum class TypeLayout : uint32_t - { - AutoLayout = 0x00000000, - SequentialLayout = 0x00000008, - ExplicitLayout = 0x00000010, - }; - - enum class TypeSemantics : uint32_t - { - Class = 0x00000000, - Interface = 0x00000020, - }; - - enum class StringFormat : uint32_t - { - AnsiClass = 0x00000000, - UnicodeClass = 0x00010000, - AutoClass = 0x00020000, - CustomFormatClass = 0x00030000, - CustomFormatMask = 0x00C00000, - }; - - enum class CodeType : uint16_t - { - IL = 0x0000, // Method impl is CIL - Native = 0x0001, // Method impl is native - OPTIL = 0x0002, // Reserved: shall be zero in conforming implementations - Runtime = 0x0003, // Method impl is provided by the runtime - }; - - enum class Managed : uint16_t - { - Unmanaged = 0x0004, - Managed = 0x0000, - }; - - enum class VtableLayout : uint16_t - { - ReuseSlot = 0x0000, // Method reuses existing slot in a vtable - NewSlot = 0x0100, // Method always gets a new slot in the vtable - }; - - enum class GenericParamVariance : uint16_t - { - None = 0x0000, - Covariant = 0x0001, - Contravariant = 0x0002 - }; - - enum class GenericParamSpecialConstraint : uint16_t - { - ReferenceTypeConstraint = 0x0004, - NotNullableValueTypeConstraint = 0x0008, - DefaultConstructorConstraint = 0x0010 - }; - - enum class ConstantType : uint16_t - { - Boolean = 0x02, - Char = 0x03, - Int8 = 0x04, - UInt8 = 0x05, - Int16 = 0x06, - UInt16 = 0x07, - Int32 = 0x08, - UInt32 = 0x09, - Int64 = 0x0a, - UInt64 = 0x0b, - Float32 = 0x0c, - Float64 = 0x0d, - String = 0x0e, - Class = 0x12 - }; - - enum class ElementType : uint8_t - { - End = 0x00, // Sentinel value - - Void = 0x01, - Boolean = 0x02, - Char = 0x03, - I1 = 0x04, - U1 = 0x05, - I2 = 0x06, - U2 = 0x07, - I4 = 0x08, - U4 = 0x09, - I8 = 0x0a, - U8 = 0x0b, - R4 = 0x0c, - R8 = 0x0d, - String = 0x0e, - - Ptr = 0x0f, // Followed by TypeSig - ByRef = 0x10, // Followed by TypeSig - ValueType = 0x11, // Followed by TypeDef or TypeRef - Class = 0x12, // Followed by TypeDef or TypeRef - Var = 0x13, // Generic parameter in a type definition, represented as unsigned integer - Array = 0x14, - GenericInst = 0x15, - TypedByRef = 0x16, - - I = 0x18, // System.IntPtr - U = 0x19, // System.UIntPtr - - FnPtr = 0x1b, // Followed by full method signature - Object = 0x1c, // System.Object - SZArray = 0x1d, - MVar = 0x1e, // Generic parameter in a method definition, represented as unsigned integer - CModReqd = 0x1f, // Required modifier, followed by a TypeDef or TypeRef - CModOpt = 0x20, // Optional modifier, followed by a TypeDef or TypeRef - Internal = 0x21, - - Modifier = 0x40, // Or'd with folowing element types - Sentinel = 0x41, // Sentinel for vararg method signature - - Pinned = 0x45, - - Type = 0x50, // System.Type - TaggedObject = 0x51, // Boxed object (in custom attributes) - Field = 0x53, // Custom attribute field - Property = 0x54, // Custom attribute property - Enum = 0x55, // Custom attribute enum - }; - - enum class CallingConvention : uint8_t - { - Default = 0x00, - VarArg = 0x05, - Field = 0x06, - LocalSig = 0x07, - Property = 0x08, - GenericInst = 0x10, - Mask = 0x0f, - - HasThis = 0x20, - ExplicitThis = 0x40, - Generic = 0x10, - }; - - enum class AssemblyHashAlgorithm : uint32_t - { - None = 0x0000, - Reserved_MD5 = 0x8003, - SHA1 = 0x8004, - }; - - enum class AssemblyFlags : uint32_t - { - PublicKey = 0x0001, // The assembly reference holds the full (unhashed) public key - Retargetable = 0x0100, - WindowsRuntime = 0x0200, - DisableJITcompileOptimizer = 0x4000, - EnableJITcompileTracking = 0x8000, - }; - - template - constexpr inline T enum_mask(T value, T mask) noexcept - { - static_assert(std::is_enum_v); - using val = std::underlying_type_t; - return static_cast(static_cast(value) & static_cast(mask)); - } -} diff --git a/natvis/winmd/impl/winmd_reader/enum_traits.h b/natvis/winmd/impl/winmd_reader/enum_traits.h deleted file mode 100644 index 7d33d40e3..000000000 --- a/natvis/winmd/impl/winmd_reader/enum_traits.h +++ /dev/null @@ -1,170 +0,0 @@ - -namespace winmd::reader -{ - struct Module; - struct TypeRef; - struct TypeDef; - struct Field; - struct MethodDef; - struct Param; - struct InterfaceImpl; - struct MemberRef; - struct Constant; - struct CustomAttribute; - struct FieldMarshal; - struct DeclSecurity; - struct ClassLayout; - struct FieldLayout; - struct StandAloneSig; - struct EventMap; - struct Event; - struct PropertyMap; - struct Property; - struct MethodSemantics; - struct MethodImpl; - struct ModuleRef; - struct TypeSpec; - struct ImplMap; - struct FieldRVA; - struct Assembly; - struct AssemblyProcessor; - struct AssemblyOS; - struct AssemblyRef; - struct AssemblyRefProcessor; - struct AssemblyRefOS; - struct File; - struct ExportedType; - struct ManifestResource; - struct NestedClass; - struct GenericParam; - struct MethodSpec; - struct GenericParamConstraint; - - - template - struct index_tag; - - template - inline constexpr auto index_tag_v = index_tag::value; - - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; - - template<> - struct index_tag : std::integral_constant {}; - template<> - struct index_tag : std::integral_constant {}; -} diff --git a/natvis/winmd/impl/winmd_reader/filter.h b/natvis/winmd/impl/winmd_reader/filter.h deleted file mode 100644 index e682f358f..000000000 --- a/natvis/winmd/impl/winmd_reader/filter.h +++ /dev/null @@ -1,136 +0,0 @@ - -namespace winmd::reader -{ - struct filter - { - filter() noexcept = default; - - template - filter(T const& includes, T const& excludes) - { - for (auto&& include : includes) - { - m_rules.push_back({ include, true }); - } - - for (auto&& exclude : excludes) - { - m_rules.push_back({ exclude, false }); - } - - std::sort(m_rules.begin(), m_rules.end(), [](auto const& lhs, auto const& rhs) - { - return std::pair{ lhs.first.size(), !lhs.second } > std::pair{ rhs.first.size(), !rhs.second }; - }); - } - - bool includes(TypeDef const& type) const - { - return includes(type.TypeNamespace(), type.TypeName()); - } - - bool includes(std::string_view const& type) const - { - auto position = type.find_last_of('.'); - return includes(type.substr(0, position), type.substr(position + 1)); - } - - bool includes(std::vector const& types) const - { - if (m_rules.empty()) - { - return true; - } - - for (auto&& type : types) - { - if (includes(type.TypeNamespace(), type.TypeName())) - { - return true; - } - } - - return false; - } - - bool includes(cache::namespace_members const& members) const - { - if (m_rules.empty()) - { - return true; - } - - for (auto&& type : members.types) - { - if (includes(type.second.TypeNamespace(), type.second.TypeName())) - { - return true; - } - } - - return false; - } - - template - auto bind_each(std::vector const& types) const - { - return [&](auto& writer) - { - for (auto&& type : types) - { - if (includes(type)) - { - F(writer, type); - } - } - }; - } - - bool empty() const noexcept - { - return m_rules.empty(); - } - - private: - - bool includes(std::string_view const& type_namespace, std::string_view const& type_name) const noexcept - { - if (m_rules.empty()) - { - return true; - } - - for (auto&& rule : m_rules) - { - if (match(type_namespace, type_name, rule.first)) - { - return rule.second; - } - } - - return false; - } - - static bool match(std::string_view const& type_namespace, std::string_view const& type_name, std::string_view const& match) noexcept - { - if (match.size() <= type_namespace.size()) - { - return impl::starts_with(type_namespace, match); - } - - if (!impl::starts_with(match, type_namespace)) - { - return false; - } - - if (match[type_namespace.size()] != '.') - { - return false; - } - - return impl::starts_with(type_name, match.substr(type_namespace.size() + 1)); - } - - std::vector> m_rules; - }; -} diff --git a/natvis/winmd/impl/winmd_reader/flags.h b/natvis/winmd/impl/winmd_reader/flags.h deleted file mode 100644 index 6ae8ebfe2..000000000 --- a/natvis/winmd/impl/winmd_reader/flags.h +++ /dev/null @@ -1,660 +0,0 @@ - -namespace winmd::impl -{ - template - struct AttributesBase - { - T value; - protected: - constexpr T get_mask(T mask) const noexcept - { - return value & mask; - } - void set_mask(T arg, T mask) noexcept - { - value = (value & ~mask) | (arg & mask); - } - template - constexpr U get_enum(T mask) const noexcept - { - static_assert(std::is_enum_v); - static_assert(std::is_same_v>); - return static_cast(get_mask(mask)); - } - template - void set_enum(U arg, T mask) noexcept - { - static_assert(std::is_enum_v); - static_assert(std::is_same_v>); - set_mask(static_cast(arg), mask); - } - constexpr bool get_bit(int bit) const noexcept - { - return get_mask(1 << bit); - } - void set_bit(bool arg, int bit) noexcept - { - set_mask(arg << bit, 1 << bit); - } - }; -} - -namespace winmd::reader -{ - struct AssemblyAttributes : impl::AttributesBase - { - constexpr bool WindowsRuntime() const noexcept - { - return get_bit(WindowsRuntime_bit); - } - void WindowsRuntime(bool arg) noexcept - { - set_bit(arg, WindowsRuntime_bit); - } - - private: - static constexpr int WindowsRuntime_bit{ 9 }; - }; - - struct EventAttributes : impl::AttributesBase - { - constexpr bool SpecialName() const noexcept - { - return get_bit(SpecialName_bit); - } - void SpecialName(bool arg) noexcept - { - set_bit(arg, SpecialName_bit); - } - constexpr bool RTSpecialName() const noexcept - { - return get_bit(RTSpecialName_bit); - } - void RTSpecialName(bool arg) noexcept - { - set_bit(arg, RTSpecialName_bit); - } - - private: - static constexpr int SpecialName_bit{ 9 }; - static constexpr int RTSpecialName_bit{ 10 }; - }; - - struct FieldAttributes : impl::AttributesBase - { - constexpr MemberAccess Access() const noexcept - { - return get_enum(Access_mask); - } - void Access(MemberAccess arg) noexcept - { - set_enum(arg, Access_mask); - } - constexpr bool Static() const noexcept - { - return get_bit(Static_bit); - } - constexpr bool InitOnly() const noexcept - { - return get_bit(InitOnly_bit); - } - constexpr bool Literal() const noexcept - { - return get_bit(Literal_bit); - } - constexpr bool NotSerialized() const noexcept - { - return get_bit(NotSerialized_bit); - } - constexpr bool SpecialName() const noexcept - { - return get_bit(SpecialName_bit); - } - constexpr bool PInvokeImpl() const noexcept - { - return get_bit(PInvokeImpl_bit); - } - constexpr bool RTSpecialName() const noexcept - { - return get_bit(RTSpecialName_bit); - } - constexpr bool HasFieldMarshal() const noexcept - { - return get_bit(HasFieldMarshal_bit); - } - constexpr bool HasDefault() const noexcept - { - return get_bit(HasDefault_bit); - } - constexpr bool HasFieldRVA() const noexcept - { - return get_bit(HasFieldRVA_bit); - } - - private: - static constexpr uint16_t Access_mask{ 0x0007 }; - static constexpr int Static_bit{ 4 }; - static constexpr int InitOnly_bit{ 5 }; - static constexpr int Literal_bit{ 6 }; - static constexpr int NotSerialized_bit{ 7 }; - static constexpr int SpecialName_bit{ 9 }; - static constexpr int PInvokeImpl_bit{ 13 }; - static constexpr int RTSpecialName_bit{ 10 }; - static constexpr int HasFieldMarshal_bit{ 12 }; - static constexpr int HasDefault_bit{ 15 }; - static constexpr int HasFieldRVA_bit{ 8 }; - }; - - struct GenericParamAttributes : impl::AttributesBase - { - constexpr GenericParamVariance Variance() const noexcept - { - return get_enum(Variance_mask); - } - void Variance(GenericParamVariance arg) noexcept - { - set_enum(arg, Variance_mask); - } - constexpr GenericParamSpecialConstraint SpecialConstraint() const noexcept - { - return get_enum(SpecialConstraint_mask); - } - void SpecialConstraint(GenericParamSpecialConstraint arg) noexcept - { - set_enum(arg, SpecialConstraint_mask); - } - - private: - static constexpr uint16_t Variance_mask{ 0x0003 }; - static constexpr uint16_t SpecialConstraint_mask{ 0x001c }; - }; - - struct MethodAttributes : impl::AttributesBase - { - constexpr MemberAccess Access() const noexcept - { - return get_enum(Access_mask); - } - void Access(MemberAccess arg) noexcept - { - set_enum(arg, Access_mask); - } - constexpr bool Static() const noexcept - { - return get_bit(Static_bit); - } - void Static(bool arg) noexcept - { - set_bit(arg, Static_bit); - } - constexpr bool Final() const noexcept - { - return get_bit(Final_bit); - } - void Final(bool arg) noexcept - { - set_bit(arg, Final_bit); - } - constexpr bool Virtual() const noexcept - { - return get_bit(Virtual_bit); - } - void Virtual(bool arg) noexcept - { - set_bit(arg, Virtual_bit); - } - constexpr bool HideBySig() const noexcept - { - return get_bit(HideBySig_bit); - } - void HideBySig(bool arg) noexcept - { - set_bit(arg, HideBySig_bit); - } - constexpr VtableLayout Layout() const noexcept - { - return get_enum(VtableLayout_mask); - } - void Layout(VtableLayout arg) noexcept - { - set_enum(arg, VtableLayout_mask); - } - constexpr bool Strict() const noexcept - { - return get_bit(Strict_bit); - } - void Strict(bool arg) noexcept - { - set_bit(arg, Strict_bit); - } - constexpr bool Abstract() const noexcept - { - return get_bit(Abstract_bit); - } - void Abstract(bool arg) noexcept - { - set_bit(arg, Abstract_bit); - } - constexpr bool SpecialName() const noexcept - { - return get_bit(SpecialName_bit); - } - void SpecialName(bool arg) noexcept - { - set_bit(arg, SpecialName_bit); - } - constexpr bool PInvokeImpl() const noexcept - { - return get_bit(PInvokeImpl_bit); - } - void PInvokeImpl(bool arg) noexcept - { - set_bit(arg, PInvokeImpl_bit); - } - constexpr bool UnmanagedExport() const noexcept - { - return get_bit(UnmanagedExport_bit); - } - void UnmanagedExport(bool arg) noexcept - { - set_bit(arg, UnmanagedExport_bit); - } - constexpr bool RTSpecialName() const noexcept - { - return get_bit(RTSpecialName_bit); - } - void RTSpecialName(bool arg) noexcept - { - set_bit(arg, RTSpecialName_bit); - } - constexpr bool HasSecurity() const noexcept - { - return get_bit(HasSecurity_bit); - } - void HasSecurity(bool arg) noexcept - { - set_bit(arg, HasSecurity_bit); - } - constexpr bool RequireSecObject() const noexcept - { - return get_bit(RequireSecObject_bit); - } - void RequireSecObject(bool arg) noexcept - { - set_bit(arg, RequireSecObject_bit); - } - - private: - static constexpr uint16_t Access_mask{ 0x0007 }; - static constexpr int Static_bit{ 4 }; - static constexpr int Final_bit{ 5 }; - static constexpr int Virtual_bit{ 6 }; - static constexpr int HideBySig_bit{ 7 }; - static constexpr uint16_t VtableLayout_mask{ 0x0100 }; - static constexpr int Strict_bit{ 9 }; - static constexpr int Abstract_bit{ 10 }; - static constexpr int SpecialName_bit{ 11 }; - static constexpr int PInvokeImpl_bit{ 13 }; - static constexpr int UnmanagedExport_bit{ 3 }; - static constexpr int RTSpecialName_bit{ 12 }; - static constexpr int HasSecurity_bit{ 14 }; - static constexpr int RequireSecObject_bit{ 15 }; - }; - - struct MethodImplAttributes : impl::AttributesBase - { - constexpr reader::CodeType CodeType() const noexcept - { - return get_enum(CodeType_mask); - } - void CodeType(reader::CodeType arg) noexcept - { - set_enum(arg, CodeType_mask); - } - constexpr reader::Managed Managed() const noexcept - { - return get_enum(Managed_mask); - } - void Managed(reader::Managed arg) noexcept - { - set_enum(arg, Managed_mask); - } - constexpr bool ForwardRef() const noexcept - { - return get_bit(ForwardRef_bit); - } - void ForwardRef(bool arg) noexcept - { - set_bit(arg, ForwardRef_bit); - } - constexpr bool PreserveSig() const noexcept - { - return get_bit(PreserveSig_bit); - } - void PreserveSig(bool arg) noexcept - { - set_bit(arg, PreserveSig_bit); - } - constexpr bool InternalCall() const noexcept - { - return get_bit(InternalCall_bit); - } - void InternalCall(bool arg) noexcept - { - set_bit(arg, InternalCall_bit); - } - constexpr bool Synchronized() const noexcept - { - return get_bit(Synchronized_bit); - } - void Synchronized(bool arg) noexcept - { - set_bit(arg, Synchronized_bit); - } - constexpr bool NoInlining() const noexcept - { - return get_bit(NoInlining_bit); - } - void NoInlining(bool arg) noexcept - { - set_bit(arg, NoInlining_bit); - } - constexpr bool NoOptimization() const noexcept - { - return get_bit(NoOptimization_bit); - } - void NoOptimization(bool arg) noexcept - { - set_bit(arg, NoOptimization_bit); - } - - private: - static constexpr uint16_t CodeType_mask{ 0x0003 }; - static constexpr uint16_t Managed_mask{ 0x0004 }; - static constexpr int ForwardRef_bit{ 4 }; // Method is defined; used primarily in merge scenarios - static constexpr int PreserveSig_bit{ 7 }; // Reserved - static constexpr int InternalCall_bit{ 12 }; // Reserved - static constexpr int Synchronized_bit{ 5 }; // Method is single threaded through the body - static constexpr int NoInlining_bit{ 3 }; // Method cannot be inlined - static constexpr int NoOptimization_bit{ 6 }; // Method will not be optimized when generatinv native code - static constexpr uint16_t MaxMethodImplVal{ 0xffff }; // Range check value - }; - - struct MethodSemanticsAttributes : impl::AttributesBase - { - constexpr bool Setter() const noexcept - { - return get_bit(Setter_bit); - } - void Setter(bool arg) noexcept - { - set_bit(arg, Setter_bit); - } - constexpr bool Getter() const noexcept - { - return get_bit(Getter_bit); - } - void Getter(bool arg) noexcept - { - set_bit(arg, Getter_bit); - } - constexpr bool Other() const noexcept - { - return get_bit(Other_bit); - } - void Other(bool arg) noexcept - { - set_bit(arg, Other_bit); - } - constexpr bool AddOn() const noexcept - { - return get_bit(AddOn_bit); - } - void AddOn(bool arg) noexcept - { - set_bit(arg, AddOn_bit); - } - constexpr bool RemoveOn() const noexcept - { - return get_bit(RemoveOn_bit); - } - void RemoveOn(bool arg) noexcept - { - set_bit(arg, RemoveOn_bit); - } - constexpr bool Fire() const noexcept - { - return get_bit(Fire_bit); - } - void Fire(bool arg) noexcept - { - set_bit(arg, Fire_bit); - } - - private: - static constexpr int Setter_bit{ 0 }; - static constexpr int Getter_bit{ 1 }; - static constexpr int Other_bit{ 2 }; - static constexpr int AddOn_bit{ 3 }; - static constexpr int RemoveOn_bit{ 4 }; - static constexpr int Fire_bit{ 5 }; - }; - - struct ParamAttributes : impl::AttributesBase - { - constexpr bool In() const noexcept - { - return get_bit(In_bit); - } - void In(bool arg) noexcept - { - set_bit(arg, In_bit); - } - constexpr bool Out() const noexcept - { - return get_bit(Out_bit); - } - void Out(bool arg) noexcept - { - set_bit(arg, Out_bit); - } - constexpr bool Optional() const noexcept - { - return get_bit(Optional_bit); - } - void Optional(bool arg) noexcept - { - set_bit(arg, Optional_bit); - } - constexpr bool HasDefault() const noexcept - { - return get_bit(HasDefault_bit); - } - void HasDefault(bool arg) noexcept - { - set_bit(arg, HasDefault_bit); - } - constexpr bool HasFieldMarshal() const noexcept - { - return get_bit(HasFieldMarshal_bit); - } - void HasFieldMarshal(bool arg) noexcept - { - set_bit(arg, HasFieldMarshal_bit); - } - - private: - static constexpr int In_bit{ 0 }; - static constexpr int Out_bit{ 1 }; - static constexpr int Optional_bit{ 4 }; - static constexpr int HasDefault_bit{ 12 }; - static constexpr int HasFieldMarshal_bit{ 13 }; - static constexpr uint16_t Unused_mask{ 0xcfe0 }; - }; - - struct PropertyAttributes : impl::AttributesBase - { - constexpr bool SpecialName() const noexcept - { - return get_bit(SpecialName_bit); - } - void SpecialName(bool arg) noexcept - { - set_bit(arg, SpecialName_bit); - } - constexpr bool RTSpecialName() const noexcept - { - return get_bit(RTSpecialName_bit); - } - void RTSpecialName(bool arg) noexcept - { - set_bit(arg, RTSpecialName_bit); - } - constexpr bool HasDefault() const noexcept - { - return get_bit(HasDefault_bit); - } - void HasDefault(bool arg) noexcept - { - set_bit(arg, HasDefault_bit); - } - - private: - static constexpr int SpecialName_bit{ 9 }; - static constexpr int RTSpecialName_bit{ 10 }; - static constexpr int HasDefault_bit{ 12 }; - static constexpr uint16_t Unused_mask{ 0xe9ff }; - }; - - struct TypeAttributes : impl::AttributesBase - { - constexpr TypeVisibility Visibility() const noexcept - { - return get_enum(Visibility_mask); - } - void Visibility(TypeVisibility arg) noexcept - { - set_enum(arg, Visibility_mask); - } - constexpr TypeLayout Layout() const noexcept - { - return get_enum(Layout_mask); - } - void Layout(TypeLayout arg) noexcept - { - set_enum(arg, Layout_mask); - } - constexpr TypeSemantics Semantics() const noexcept - { - return get_enum(Semantics_mask); - } - void Semantics(TypeSemantics arg) noexcept - { - set_enum(arg, Semantics_mask); - } - constexpr bool Abstract() const noexcept - { - return get_bit(Abstract_bit); - } - void Abstract(bool arg) noexcept - { - set_bit(arg, Abstract_bit); - } - constexpr bool Sealed() const noexcept - { - return get_bit(Sealed_bit); - } - void Sealed(bool arg) noexcept - { - set_bit(arg, Sealed_bit); - } - constexpr bool SpecialName() const noexcept - { - return get_bit(SpecialName_bit); - } - void SpecialName(bool arg) noexcept - { - set_bit(arg, SpecialName_bit); - } - constexpr bool Import() const noexcept - { - return get_bit(Import_bit); - } - void Import(bool arg) noexcept - { - set_bit(arg, Import_bit); - } - constexpr bool Serializable() const noexcept - { - return get_bit(Serializable_bit); - } - void Serializable(bool arg) noexcept - { - set_bit(arg, Serializable_bit); - } - constexpr bool WindowsRuntime() const noexcept - { - return get_bit(WindowsRuntime_bit); - } - void WindowsRuntime(bool arg) noexcept - { - set_bit(arg, WindowsRuntime_bit); - } - constexpr reader::StringFormat StringFormat() const noexcept - { - return get_enum(StringFormat_mask); - } - void StringFormat(reader::StringFormat arg) noexcept - { - set_enum(arg, StringFormat_mask); - } - constexpr bool BeforeFieldInit() const noexcept - { - return get_bit(BeforeFieldInit_bit); - } - void BeforeFieldInit(bool arg) noexcept - { - set_bit(arg, BeforeFieldInit_bit); - } - constexpr bool RTSpecialName() const noexcept - { - return get_bit(RTSpecialName_bit); - } - void RTSpecialName(bool arg) noexcept - { - set_bit(arg, RTSpecialName_bit); - } - constexpr bool HasSecurity() const noexcept - { - return get_bit(HasSecurity_bit); - } - void HasSecurity(bool arg) noexcept - { - set_bit(arg, HasSecurity_bit); - } - constexpr bool IsTypeForwarder() const noexcept - { - return get_bit(IsTypeForwarder_bit); - } - void IsTypeForwarder(bool arg) noexcept - { - set_bit(arg, IsTypeForwarder_bit); - } - - private: - static constexpr uint32_t Visibility_mask{ 0x00000007 }; - static constexpr uint32_t Layout_mask{ 0x00000018 }; - static constexpr uint32_t Semantics_mask{ 0x00000020 }; - static constexpr int Abstract_bit{ 7 }; - static constexpr int Sealed_bit{ 8 }; - static constexpr int SpecialName_bit{ 10 }; - static constexpr int Import_bit{ 12 }; - static constexpr int Serializable_bit{ 13 }; - static constexpr int WindowsRuntime_bit{ 14 }; - static constexpr uint32_t StringFormat_mask{ 0x00030000 }; - static constexpr int BeforeFieldInit_bit{ 20 }; - static constexpr int RTSpecialName_bit{ 11 }; - static constexpr int HasSecurity_bit{ 18 }; - static constexpr int IsTypeForwarder_bit{ 21 }; - }; -} diff --git a/natvis/winmd/impl/winmd_reader/helpers.h b/natvis/winmd/impl/winmd_reader/helpers.h deleted file mode 100644 index 75ecc3b8b..000000000 --- a/natvis/winmd/impl/winmd_reader/helpers.h +++ /dev/null @@ -1,129 +0,0 @@ - -namespace winmd::reader -{ - template - bool empty(std::pair const& range) noexcept - { - return range.first == range.second; - } - - template - std::size_t size(std::pair const& range) noexcept - { - return range.second - range.first; - } - - inline auto find(TypeRef const& type) - { - 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) - { - 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) - { - if (type.type() == TypeDefOrRef::TypeRef) - { - return find_required(type.TypeRef()); - } - else if (type.type() == TypeDefOrRef::TypeDef) - { - return type.TypeDef(); - } - else - { - XLANG_ASSERT(false); - return {}; - } - } - - inline bool is_const(ParamSig const& param) - { - auto is_type_const = [](auto&& type) - { - return type.TypeNamespace() == "System.Runtime.CompilerServices" && type.TypeName() == "IsConst"; - }; - - for (auto const& cmod : param.CustomMod()) - { - auto type = cmod.Type(); - - if (type.type() == TypeDefOrRef::TypeDef) - { - if (is_type_const(type.TypeDef())) - { - return true; - } - } - else if (type.type() == TypeDefOrRef::TypeRef) - { - if (is_type_const(type.TypeRef())) - { - return true; - } - } - } - - return false; - }; -} diff --git a/natvis/winmd/impl/winmd_reader/index.h b/natvis/winmd/impl/winmd_reader/index.h deleted file mode 100644 index 26de81f10..000000000 --- a/natvis/winmd/impl/winmd_reader/index.h +++ /dev/null @@ -1,56 +0,0 @@ - -namespace winmd::reader -{ - template <> struct typed_index : index_base - { - using index_base::index_base; - - auto MemberRef() const; - auto MethodDef() const; - }; - - template <> struct typed_index : index_base - { - using index_base::index_base; - - auto Field() const; - auto Param() const; - auto Property() const; - }; - - template <> struct typed_index : index_base - { - using index_base::index_base; - - auto Property() const; - auto Event() const; - }; - - template <> struct typed_index : index_base - { - using index_base::index_base; - - auto MethodDef() const; - auto MemberRef() const; - }; - - template <> struct typed_index : index_base - { - using index_base::index_base; - - auto Module() const; - auto ModuleRef() const; - auto AssemblyRef() const; - auto TypeRef() const; - }; - - template <> struct typed_index : index_base - { - using index_base::index_base; - - reader::TypeDef TypeDef() const; - reader::TypeRef TypeRef() const; - reader::TypeSpec TypeSpec() const; - auto CustomAttribute() const; - }; -} diff --git a/natvis/winmd/impl/winmd_reader/key.h b/natvis/winmd/impl/winmd_reader/key.h deleted file mode 100644 index f26aefaac..000000000 --- a/natvis/winmd/impl/winmd_reader/key.h +++ /dev/null @@ -1,213 +0,0 @@ - -namespace winmd::reader -{ - template - template - Row index_base::get_row() const - { - XLANG_ASSERT(type() == (index_tag_v)); - return get_database().template get_table()[index()]; - } - - inline auto typed_index::MemberRef() const - { - return get_row(); - } - - inline auto typed_index::MethodDef() const - { - return get_row(); - } - - inline auto typed_index::Field() const - { - return get_row(); - } - - inline auto typed_index::Param() const - { - return get_row(); - } - - inline auto typed_index::Property() const - { - return get_row(); - } - - inline auto typed_index::Property() const - { - return get_row(); - } - - inline auto typed_index::Event() const - { - return get_row(); - } - - inline auto typed_index::MethodDef() const - { - return get_row(); - } - - inline auto typed_index::MemberRef() const - { - return get_row(); - } - - inline auto typed_index::Module() const - { - return get_row(); - } - - inline auto typed_index::ModuleRef() const - { - return get_row(); - } - - inline auto typed_index::AssemblyRef() const - { - return get_row(); - } - - inline auto typed_index::TypeRef() const - { - return get_row(); - } - - inline TypeDef typed_index::TypeDef() const - { - return get_row(); - } - - inline TypeRef typed_index::TypeRef() const - { - return get_row(); - } - - inline TypeSpec typed_index::TypeSpec() const - { - return get_row(); - } - - inline auto typed_index::CustomAttribute() const - { - if (type() == TypeDefOrRef::TypeDef) - { - return TypeDef().CustomAttribute(); - } - - if (type() == TypeDefOrRef::TypeRef) - { - return TypeRef().CustomAttribute(); - } - - return TypeSpec().CustomAttribute(); - } - - inline auto typed_index::TypeRef() const - { - return get_row(); - } - - inline auto typed_index::TypeDef() const - { - return get_row(); - } - - inline bool TypeDef::is_enum() const - { - return extends_type(*this, "System"sv, "Enum"sv); - } - - struct EnumDefinition - { - explicit EnumDefinition(TypeDef const& type) - : m_typedef(type) - { - XLANG_ASSERT(type.is_enum()); - for (auto field : type.FieldList()) - { - if (!field.Flags().Literal() && !field.Flags().Static()) - { - XLANG_ASSERT(m_underlying_type == ElementType::End); - m_underlying_type = std::get(field.Signature().Type().Type()); - XLANG_ASSERT(ElementType::Boolean <= m_underlying_type && m_underlying_type <= ElementType::U8); - } - } - } - - auto get_enumerator(std::string_view const& name) const - { - auto fields = m_typedef.FieldList(); - - auto field = std::find_if(begin(fields), end(fields), [&](auto&& field) - { - return field.Name() == name; - }); - - XLANG_ASSERT(field != end(fields)); - return field; - } - - TypeDef m_typedef; - ElementType m_underlying_type{}; - - }; - - inline auto TypeDef::get_enum_definition() const - { - return EnumDefinition{ *this }; - } - - template - CustomAttribute get_attribute(T const& row, std::string_view const& type_namespace, std::string_view const& type_name) - { - for (auto&& attribute : row.CustomAttribute()) - { - auto pair = attribute.TypeNamespaceAndName(); - - if (pair.first == type_namespace && pair.second == type_name) - { - return attribute; - } - } - - 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/natvis/winmd/impl/winmd_reader/pe.h b/natvis/winmd/impl/winmd_reader/pe.h deleted file mode 100644 index ce25498de..000000000 --- a/natvis/winmd/impl/winmd_reader/pe.h +++ /dev/null @@ -1,163 +0,0 @@ - -namespace winmd::impl -{ - struct image_dos_header - { - uint16_t e_signature; - uint16_t e_cblp; - uint16_t e_cp; - uint16_t e_crlc; - uint16_t e_cparhdr; - uint16_t e_minalloc; - uint16_t e_maxalloc; - uint16_t e_ss; - uint16_t e_sp; - uint16_t e_csum; - uint16_t e_ip; - uint16_t e_cs; - uint16_t e_lfarlc; - uint16_t e_ovno; - uint16_t e_res[4]; - uint16_t e_oemid; - uint16_t e_oeminfo; - uint16_t e_res2[10]; - int32_t e_lfanew; - }; - - struct image_file_header - { - uint16_t Machine; - uint16_t NumberOfSections; - uint32_t TimeDateStamp; - uint32_t PointerToSymbolTable; - uint32_t NumberOfSymbols; - uint16_t SizeOfOptionalHeader; - uint16_t Characteristics; - }; - - struct image_data_directory - { - uint32_t VirtualAddress; - uint32_t Size; - }; - - struct image_optional_header32 - { - uint16_t Magic; - uint8_t MajorLinkerVersion; - uint8_t MinorLinkerVersion; - uint32_t SizeOfCode; - uint32_t SizeOfInitializedData; - uint32_t SizeOfUninitializedData; - uint32_t AddressOfEntryPoint; - uint32_t BaseOfCode; - uint32_t BaseOfData; - uint32_t ImageBase; - uint32_t SectionAlignment; - uint32_t FileAlignment; - uint16_t MajorOperatingSystemVersion; - uint16_t MinorOperatingSystemVersion; - uint16_t MajorImageVersion; - uint16_t MinorImageVersion; - uint16_t MajorSubsystemVersion; - uint16_t MinorSubsystemVersion; - uint32_t Win32VersionValue; - uint32_t SizeOfImage; - uint32_t SizeOfHeaders; - uint32_t CheckSum; - uint16_t Subsystem; - uint16_t DllCharacteristics; - uint32_t SizeOfStackReserve; - uint32_t SizeOfStackCommit; - uint32_t SizeOfHeapReserve; - uint32_t SizeOfHeapCommit; - uint32_t LoaderFlags; - uint32_t NumberOfRvaAndSizes; - image_data_directory DataDirectory[16]; - }; - - struct image_nt_headers32 - { - uint32_t Signature; - image_file_header FileHeader; - image_optional_header32 OptionalHeader; - }; - - struct image_optional_header32plus - { - uint16_t Magic; - uint8_t MajorLinkerVersion; - uint8_t MinorLinkerVersion; - uint32_t SizeOfCode; - uint32_t SizeOfInitializedData; - uint32_t SizeOfUninitializedData; - uint32_t AddressOfEntryPoint; - uint32_t BaseOfCode; - uint64_t ImageBase; - uint32_t SectionAlignment; - uint32_t FileAlignment; - uint16_t MajorOperatingSystemVersion; - uint16_t MinorOperatingSystemVersion; - uint16_t MajorImageVersion; - uint16_t MinorImageVersion; - uint16_t MajorSubsystemVersion; - uint16_t MinorSubsystemVersion; - uint32_t Win32VersionValue; - uint32_t SizeOfImage; - uint32_t SizeOfHeaders; - uint32_t CheckSum; - uint16_t Subsystem; - uint16_t DllCharacteristics; - uint64_t SizeOfStackReserve; - uint64_t SizeOfStackCommit; - uint64_t SizeOfHeapReserve; - uint64_t SizeOfHeapCommit; - uint32_t LoaderFlags; - uint32_t NumberOfRvaAndSizes; - image_data_directory DataDirectory[16]; - }; - - struct image_nt_headers32plus - { - uint32_t Signature; - image_file_header FileHeader; - image_optional_header32plus OptionalHeader; - }; - - struct image_section_header { - uint8_t Name[8]; // IMAGE_SIZEOF_SHORT_NAME - union - { - uint32_t PhysicalAddress; - uint32_t VirtualSize; - } Misc; - uint32_t VirtualAddress; - uint32_t SizeOfRawData; - uint32_t PointerToRawData; - uint32_t PointerToRelocations; - uint32_t PointerToLinenumbers; - uint16_t NumberOfRelocations; - uint16_t NumberOfLinenumbers; - uint32_t Characteristics; - }; - - struct image_cor20_header - { - uint32_t cb; - uint16_t MajorRuntimeVersion; - uint16_t MinorRuntimeVersion; - image_data_directory MetaData; - uint32_t Flags; - union - { - uint32_t EntryPointToken; - uint32_t EntryPointRVA; - } dummyunionname; - image_data_directory Resources; - image_data_directory StrongNameSignature; - image_data_directory CodeManagerTable; - image_data_directory VTableFixups; - image_data_directory ExportAddressTableJumps; - image_data_directory ManagedNativeHeader; - }; -} diff --git a/natvis/winmd/impl/winmd_reader/schema.h b/natvis/winmd/impl/winmd_reader/schema.h deleted file mode 100644 index c3ba25da5..000000000 --- a/natvis/winmd/impl/winmd_reader/schema.h +++ /dev/null @@ -1,694 +0,0 @@ - -namespace winmd::reader -{ - struct TypeRef : row_base - { - using row_base::row_base; - - auto ResolutionScope() const - { - return get_coded_index(0); - } - - auto TypeName() const - { - return get_string(1); - } - - auto TypeNamespace() const - { - return get_string(2); - } - - auto CustomAttribute() const; - }; - - struct CustomAttribute : row_base - { - using row_base::row_base; - - auto Parent() const - { - return get_coded_index(0); - } - - auto Type() const - { - return get_coded_index(1); - } - - auto Value() const; - - auto TypeNamespaceAndName() const; - }; - - struct TypeDef : row_base - { - using row_base::row_base; - - auto Flags() const - { - return TypeAttributes{{ get_value(0) }}; - } - - auto TypeName() const - { - return get_string(1); - } - - auto TypeNamespace() const - { - return get_string(2); - } - - auto Extends() const - { - return get_coded_index(3); - } - - auto FieldList() const; - auto MethodList() const; - - auto CustomAttribute() const; - auto InterfaceImpl() const; - auto GenericParam() const; - auto PropertyList() const; - auto EventList() const; - auto MethodImplList() const; - - auto EnclosingType() const; - - bool is_enum() const; - auto get_enum_definition() const; - }; - - struct MethodDef : row_base - { - using row_base::row_base; - - auto RVA() const - { - return get_value(0); - } - - auto ImplFlags() const - { - return MethodImplAttributes{{ get_value(1) }}; - } - - auto Flags() const - { - return MethodAttributes{{ get_value(2) }}; - } - - auto Name() const - { - return get_string(3); - } - - MethodDefSig Signature() const - { - auto cursor = get_blob(4); - return{ get_table(), cursor }; - } - - auto ParamList() const; - auto CustomAttribute() const; - auto Parent() const; - auto GenericParam() const; - - bool SpecialName() const - { - return Flags().SpecialName(); - } - }; - - struct MemberRef : row_base - { - using row_base::row_base; - - auto Class() const - { - return get_coded_index(0); - } - - auto Name() const - { - return get_string(1); - } - - MethodDefSig MethodSignature() const - { - auto cursor = get_blob(2); - return{ get_table(), cursor }; - } - - auto CustomAttribute() const; - }; - - struct Module : row_base - { - using row_base::row_base; - - auto Name() const - { - return get_string(1); - } - - auto CustomAttribute() const; - }; - - struct Field : row_base - { - using row_base::row_base; - - auto Flags() const - { - return FieldAttributes{{ get_value(0) }}; - } - - auto Name() const - { - return get_string(1); - } - - auto Signature() const - { - auto cursor = get_blob(2); - return FieldSig{ get_table(), cursor }; - } - - auto CustomAttribute() const; - auto Constant() const; - auto Parent() const; - auto FieldMarshal() const; - }; - - struct Param : row_base - { - using row_base::row_base; - - auto Flags() const - { - return ParamAttributes{{ get_value(0) }}; - } - - auto Sequence() const - { - return get_value(1); - } - - auto Name() const - { - return get_string(2); - } - - auto CustomAttribute() const; - auto Constant() const; - auto FieldMarshal() const; - }; - - struct InterfaceImpl : row_base - { - using row_base::row_base; - - auto Class() const; - - auto Interface() const - { - return get_coded_index(1); - } - - auto CustomAttribute() const; - }; - - struct Constant : row_base - { - using row_base::row_base; - - using constant_type = std::variant; - - auto Type() const - { - return get_value(0); - } - - auto Parent() const - { - return get_coded_index(1); - } - - auto ValueBoolean() const; - auto ValueChar() const; - auto ValueInt8() const; - auto ValueUInt8() const; - auto ValueInt16() const; - auto ValueUInt16() const; - auto ValueInt32() const; - auto ValueUInt32() const; - auto ValueInt64() const; - auto ValueUInt64() const; - auto ValueFloat32() const; - auto ValueFloat64() const; - auto ValueString() const; - auto ValueClass() const; - - constant_type Value() const; - }; - - struct FieldMarshal : row_base - { - using row_base::row_base; - - auto Parent() const - { - return get_coded_index(0); - } - }; - - struct TypeSpec : row_base - { - using row_base::row_base; - - TypeSpecSig Signature() const - { - auto cursor = get_blob(0); - return{ get_table(), cursor }; - } - - auto CustomAttribute() const; - }; - - struct DeclSecurity : row_base - { - using row_base::row_base; - }; - - struct ClassLayout : row_base - { - using row_base::row_base; - - auto PackingSize() const - { - return get_value(0); - } - - auto ClassSize() const - { - return get_value(1); - } - - auto Parent() const; - }; - - struct FieldLayout : row_base - { - using row_base::row_base; - }; - - struct StandAloneSig : row_base - { - using row_base::row_base; - - auto CustomAttribute() const; - }; - - struct EventMap : row_base - { - using row_base::row_base; - - auto Parent() const; - auto EventList() const; - }; - - struct Event : row_base - { - using row_base::row_base; - - auto EventFlags() const - { - return EventAttributes{{ get_value(0) }}; - } - - auto Name() const - { - return get_string(1); - } - - auto EventType() const - { - return get_coded_index(2); - } - - auto MethodSemantic() const; - auto Parent() const; - auto CustomAttribute() const; - }; - - struct PropertyMap : row_base - { - using row_base::row_base; - - auto Parent() const; - auto PropertyList() const; - }; - - struct Property : row_base - { - using row_base::row_base; - - auto Flags() const - { - return PropertyAttributes{{ get_value(0) }}; - } - - auto Name() const - { - return get_string(1); - } - - PropertySig Type() const - { - auto cursor = get_blob(2); - return{ get_table(), cursor }; - } - - auto MethodSemantic() const; - auto Parent() const; - auto Constant() const; - auto CustomAttribute() const; - }; - - struct MethodSemantics : row_base - { - using row_base::row_base; - - auto Semantic() const - { - return MethodSemanticsAttributes{{ get_value(0) }}; - } - - auto Method() const; - - auto Association() const - { - return get_coded_index(2); - } - }; - - struct MethodImpl : row_base - { - using row_base::row_base; - - auto Class() const; - - auto MethodBody() const - { - return get_coded_index(1); - } - - auto MethodDeclaration() const - { - return get_coded_index(2); - } - }; - - struct ModuleRef : row_base - { - using row_base::row_base; - - auto CustomAttribute() const; - }; - - struct ImplMap : row_base - { - using row_base::row_base; - }; - - struct FieldRVA : row_base - { - using row_base::row_base; - }; - - struct Assembly : row_base - { - using row_base::row_base; - - auto HashAlgId() const - { - return get_value(0); - } - - auto Version() const; - - auto Flags() const - { - return AssemblyAttributes{{ get_value(2) }}; - } - - auto PublicKey() const - { - return get_blob(3); - } - - auto Name() const - { - return get_string(4); - } - - auto Culture() const - { - return get_string(5); - } - - auto CustomAttribute() const; - }; - - struct AssemblyProcessor : row_base - { - using row_base::row_base; - - auto Processor() const - { - return get_value(0); - } - }; - - struct AssemblyOS : row_base - { - using row_base::row_base; - - auto OSPlatformId() const - { - return get_value(0); - } - - auto OSMajorVersion() const - { - return get_value(1); - } - - auto OSMinorVersion() const - { - return get_value(2); - } - }; - - struct AssemblyRef : row_base - { - using row_base::row_base; - - auto Version() const; - - auto Flags() const - { - return AssemblyAttributes{{ get_value(1) }}; - } - - auto PublicKeyOrToken() const - { - return get_blob(2); - } - - auto Name() const - { - return get_string(3); - } - - auto Culture() const - { - return get_string(4); - } - - auto HashValue() const - { - return get_string(5); - } - - auto CustomAttribute() const; - }; - - struct AssemblyRefProcessor : row_base - { - using row_base::row_base; - - auto Processor() const - { - return get_value(0); - } - - auto AssemblyRef() const; - }; - - struct AssemblyRefOS : row_base - { - using row_base::row_base; - - auto OSPlatformId() const - { - return get_value(0); - } - - auto OSMajorVersion() const - { - return get_value(1); - } - - auto OSMinorVersion() const - { - return get_value(2); - } - - auto AssemblyRef() const; - }; - - struct File : row_base - { - using row_base::row_base; - - auto CustomAttribute() const; - }; - - struct ExportedType : row_base - { - using row_base::row_base; - - auto CustomAttribute() const; - }; - - struct ManifestResource : row_base - { - using row_base::row_base; - - auto CustomAttribute() const; - }; - - struct NestedClass : row_base - { - using row_base::row_base; - - TypeDef NestedType() const; - TypeDef EnclosingType() const; - }; - - struct GenericParam : row_base - { - using row_base::row_base; - - auto Number() const - { - return get_value(0); - } - - auto Flags() const - { - return GenericParamAttributes{{ get_value(1) }}; - } - - auto Owner() const - { - return get_coded_index(2); - } - - auto Name() const - { - return get_string(3); - } - - auto CustomAttribute() const; - }; - - struct MethodSpec : row_base - { - using row_base::row_base; - - auto CustomAttribute() const; - }; - - struct GenericParamConstraint : row_base - { - using row_base::row_base; - - auto CustomAttribute() const; - }; - - inline bool operator<(coded_index const& left, CustomAttribute const& right) noexcept - { - return left < right.Parent(); - } - - inline bool operator<(CustomAttribute const& left, coded_index const& right) noexcept - { - return left.Parent() < right; - } - - inline bool operator<(coded_index const& left, GenericParam const& right) noexcept - { - return left < right.Owner(); - } - - inline bool operator<(GenericParam const& left, coded_index const& right) noexcept - { - return left.Owner() < right; - } - - inline bool operator<(coded_index const& left, Constant const& right) noexcept - { - return left < right.Parent(); - } - - inline bool operator<(Constant const& left, coded_index const& right) noexcept - { - return left.Parent() < right; - } - - inline bool operator<(coded_index const& left, MethodSemantics const& right) noexcept - { - return left < right.Association(); - } - - inline bool operator<(MethodSemantics const& left, coded_index const& right) noexcept - { - 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/natvis/winmd/impl/winmd_reader/signature.h b/natvis/winmd/impl/winmd_reader/signature.h deleted file mode 100644 index d36511554..000000000 --- a/natvis/winmd/impl/winmd_reader/signature.h +++ /dev/null @@ -1,589 +0,0 @@ - -namespace winmd::reader -{ - inline uint32_t uncompress_unsigned(byte_view& cursor) - { - auto data = cursor.begin(); - uint32_t value; - uint32_t length; - if ((*data & 0x80) == 0x00) - { - length = 1; - value = *data; - } - else if ((*data & 0xc0) == 0x80) - { - length = 2; - value = (*data++ & 0x3f) << 8; - value |= *data; - } - else if ((*data & 0xe0) == 0xc0) - { - length = 4; - value = (*data++ & 0x1f) << 24; - value |= *data++ << 16; - value |= *data++ << 8; - value |= *data; - } - else - { - impl::throw_invalid("Invalid compressed integer in blob"); - } - cursor = cursor.seek(length); - return value; - } - - template - T uncompress_enum(byte_view& cursor) - { - static_assert(std::is_enum_v); - static_assert(std::is_unsigned_v>); - return static_cast(uncompress_unsigned(cursor)); - } - - template - T read(byte_view& cursor) - { - auto const& result = cursor.as(); - cursor = cursor.seek(sizeof(T)); - return result; - } - - template <> - inline std::string_view read(byte_view& cursor) - { - uint32_t const length = uncompress_unsigned(cursor); - std::string_view result{ reinterpret_cast(cursor.begin()), length }; - cursor = cursor.seek(length); - return result; - } - - struct CustomModSig; - struct FieldSig; - struct GenericTypeInstSig; - struct MethodDefSig; - struct ParamSig; - struct PropertySig; - struct RetTypeSig; - struct TypeSig; - struct TypeSpecSig; - - struct CustomModSig - { - CustomModSig(table_base const* table, byte_view& data) - : m_cmod(uncompress_enum(data)) - , m_type(table, uncompress_unsigned(data)) - { - XLANG_ASSERT(m_cmod == ElementType::CModReqd || m_cmod == ElementType::CModOpt); - } - - ElementType CustomMod() const noexcept - { - return m_cmod; - } - - coded_index Type() const noexcept - { - return m_type; - } - - private: - ElementType m_cmod; - coded_index m_type; - }; - - struct GenericTypeInstSig - { - GenericTypeInstSig(table_base const* table, byte_view& data); - - GenericTypeInstSig(coded_index type, std::vector&& args) - : m_type(type) - , m_generic_arg_count(static_cast(args.size())) - , m_generic_args(std::move(args)) - { - // Probably don't care about m_class_or_value - } - - ElementType ClassOrValueType() const noexcept - { - return m_class_or_value; - } - - coded_index GenericType() const noexcept - { - return m_type; - } - - uint32_t GenericArgCount() const noexcept - { - return m_generic_arg_count; - } - - auto GenericArgs() const noexcept - { - return std::pair{ m_generic_args.cbegin(), m_generic_args.cend() }; - } - - private: - ElementType m_class_or_value{}; - coded_index m_type; - uint32_t m_generic_arg_count; - std::vector m_generic_args; - }; - - inline std::vector parse_cmods(table_base const* table, byte_view& data) - { - std::vector result; - auto cursor = data; - - for (auto element_type = uncompress_enum(cursor); - element_type == ElementType::CModOpt || element_type == ElementType::CModReqd; - element_type = uncompress_enum(cursor)) - { - result.emplace_back(table, data); - cursor = data; - } - return result; - } - - inline bool parse_szarray(table_base const*, byte_view& data) - { - auto cursor = data; - if (uncompress_enum(cursor) == ElementType::SZArray) - { - data = cursor; - return true; - } - 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; - }; - - struct GenericMethodTypeIndex - { - uint32_t index; - }; - - struct TypeSig - { - 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); - } - } - - explicit TypeSig(value_type&& arg) - : m_type(std::move(arg)) - { - // We probably don't actually care about m_element_type. Simply populate m_type - } - - value_type const& Type() const noexcept - { - return m_type; - } - - ElementType element_type() const noexcept - { - return m_element_type; - } - - bool is_szarray() const noexcept - { - 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) - { - auto cursor = data; - return uncompress_enum(cursor); - } - - 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) - { - auto cursor = data; - auto element_type = uncompress_enum(cursor); - if (element_type == ElementType::ByRef) - { - data = cursor; - return true; - } - else - { - XLANG_ASSERT(element_type != ElementType::TypedByRef); - return false; - } - } - - struct ParamSig - { - ParamSig(table_base const* table, byte_view& data) - : m_cmod(parse_cmods(table, data)) - , m_byref(is_by_ref(data)) - , m_type(table, data) - { - } - - auto CustomMod() const noexcept - { - return std::pair{ m_cmod.cbegin(), m_cmod.cend() }; - } - - bool ByRef() const noexcept - { - return m_byref; - } - - TypeSig const& Type() const noexcept - { - return m_type; - } - - private: - std::vector m_cmod; - bool m_byref; - TypeSig m_type; - }; - - struct RetTypeSig - { - RetTypeSig(table_base const* table, byte_view& data) - : m_cmod(parse_cmods(table, data)) - , m_byref(is_by_ref(data)) - { - auto cursor = data; - auto element_type = uncompress_enum(cursor); - if (element_type == ElementType::Void) - { - data = cursor; - } - else - { - m_type.emplace(table, data); - } - } - - auto CustomMod() const noexcept - { - return std::pair{ m_cmod.cbegin(), m_cmod.cend() }; - } - - bool ByRef() const noexcept - { - return m_byref; - } - - TypeSig const& Type() const noexcept - { - return *m_type; - } - - explicit operator bool() const noexcept - { - return m_type.has_value(); - } - - private: - std::vector m_cmod; - bool m_byref; - std::optional m_type; - }; - - struct MethodDefSig - { - MethodDefSig(table_base const* table, byte_view& data) - : m_calling_convention(uncompress_enum(data)) - , m_generic_param_count(enum_mask(m_calling_convention, CallingConvention::Generic) == CallingConvention::Generic ? uncompress_unsigned(data) : 0) - , m_param_count(uncompress_unsigned(data)) - , m_ret_type(table, data) - { - if (m_param_count > data.size()) - { - impl::throw_invalid("Invalid blob array size"); - } - m_params.reserve(m_param_count); - for (uint32_t count = 0; count < m_param_count; ++count) - { - m_params.emplace_back(table, data); - } - } - - CallingConvention CallConvention() const noexcept - { - return m_calling_convention; - } - - uint32_t GenericParamCount() const noexcept - { - return m_generic_param_count; - } - - RetTypeSig const& ReturnType() const noexcept - { - return m_ret_type; - } - - auto Params() const noexcept - { - return std::pair{ m_params.cbegin(), m_params.cend() }; - } - - private: - CallingConvention m_calling_convention; - uint32_t m_generic_param_count; - uint32_t m_param_count; - RetTypeSig m_ret_type; - std::vector m_params; - }; - - struct FieldSig - { - FieldSig(table_base const* table, byte_view& data) - : m_calling_convention(check_convention(data)) - , m_cmod(parse_cmods(table, data)) - , m_type(table, data) - {} - - auto CustomMod() const noexcept - { - return std::pair{ m_cmod.cbegin(), m_cmod.cend() }; - } - - TypeSig const& Type() const noexcept - { - return m_type; - } - - private: - static CallingConvention check_convention(byte_view& data) - { - auto conv = read(data); - if (enum_mask(conv, CallingConvention::Field) != CallingConvention::Field) - { - impl::throw_invalid("Invalid calling convention for field blob"); - } - return conv; - } - CallingConvention m_calling_convention; - std::vector m_cmod; - TypeSig m_type; - }; - - struct PropertySig - { - PropertySig(table_base const* table, byte_view& data) - : m_calling_convention(check_convention(data)) - , m_param_count(uncompress_unsigned(data)) - , m_cmod(parse_cmods(table, data)) - , m_type(table, data) - { - if (m_param_count > data.size()) - { - impl::throw_invalid("Invalid blob array size"); - } - m_params.reserve(m_param_count); - for (uint32_t count = 0; count < m_param_count; ++count) - { - m_params.emplace_back(table, data); - } - } - - TypeSig const& Type() const noexcept - { - return m_type; - } - - CallingConvention CallConvention() const noexcept - { - return m_calling_convention; - } - - private: - static CallingConvention check_convention(byte_view& data) - { - auto conv = read(data); - if (enum_mask(conv, CallingConvention::Property) != CallingConvention::Property) - { - impl::throw_invalid("Invalid calling convention for property blob"); - } - return conv; - } - CallingConvention m_calling_convention; - uint32_t m_param_count; - std::vector m_cmod; - TypeSig m_type; - std::vector m_params; - }; - - struct TypeSpecSig - { - TypeSpecSig(table_base const* table, byte_view& data) - : m_type(ParseType(table, data)) - { - } - - GenericTypeInstSig const& GenericTypeInst() const noexcept - { - return m_type; - } - - private: - static GenericTypeInstSig ParseType(table_base const* table, byte_view& data) - { - [[maybe_unused]] auto element_type = uncompress_enum(data); - XLANG_ASSERT(element_type == ElementType::GenericInst); - return { table, data }; - } - GenericTypeInstSig m_type; - }; - - inline GenericTypeInstSig::GenericTypeInstSig(table_base const* table, byte_view& data) - : m_class_or_value(uncompress_enum(data)) - , m_type(table, uncompress_unsigned(data)) - , m_generic_arg_count(uncompress_unsigned(data)) - { - if (!(m_class_or_value == ElementType::Class || m_class_or_value == ElementType::ValueType)) - { - impl::throw_invalid("Generic type instantiation signatures must begin with either ELEMENT_TYPE_CLASS or ELEMENT_TYPE_VALUE"); - } - - if (m_generic_arg_count > data.size()) - { - impl::throw_invalid("Invalid blob array size"); - } - m_generic_args.reserve(m_generic_arg_count); - for (uint32_t arg = 0; arg < m_generic_arg_count; ++arg) - { - m_generic_args.emplace_back(table, data); - } - } - - inline TypeSig::value_type TypeSig::ParseType(table_base const* table, byte_view& data) - { - auto element_type = uncompress_enum(data); - switch (element_type) - { - case ElementType::Boolean: - case ElementType::Char: - case ElementType::I1: - case ElementType::U1: - case ElementType::I2: - case ElementType::U2: - case ElementType::I4: - case ElementType::U4: - case ElementType::I8: - case ElementType::U8: - case ElementType::R4: - case ElementType::R8: - case ElementType::String: - case ElementType::Object: - case ElementType::U: - case ElementType::I: - case ElementType::Void: - return element_type; - break; - - case ElementType::Class: - case ElementType::ValueType: - return coded_index{ table, uncompress_unsigned(data) }; - break; - - case ElementType::GenericInst: - return GenericTypeInstSig{ table, data }; - break; - - case ElementType::Var: - return GenericTypeIndex{ uncompress_unsigned(data) }; - break; - - case ElementType::MVar: - return GenericMethodTypeIndex{ uncompress_unsigned(data) }; - break; - - default: - impl::throw_invalid("Unrecognized ELEMENT_TYPE encountered"); - break; - } - } -} diff --git a/natvis/winmd/impl/winmd_reader/table.h b/natvis/winmd/impl/winmd_reader/table.h deleted file mode 100644 index 20da41d0f..000000000 --- a/natvis/winmd/impl/winmd_reader/table.h +++ /dev/null @@ -1,420 +0,0 @@ - -namespace winmd::reader -{ - struct database; - struct cache; - - struct table_base - { - explicit table_base(database const* database) noexcept : m_database(database) - { - } - - database const& get_database() const noexcept - { - return *m_database; - } - - uint32_t size() const noexcept - { - return m_row_count; - } - - uint32_t row_size() const noexcept - { - return m_row_size; - } - - uint32_t column_size(uint32_t const column) const noexcept - { - return m_columns[column].size; - } - - template - T get_value(uint32_t const row, uint32_t const column) const - { - static_assert(std::is_enum_v || std::is_integral_v); - uint32_t const data_size = m_columns[column].size; - XLANG_ASSERT(data_size == 1 || data_size == 2 || data_size == 4 || data_size == 8); - XLANG_ASSERT(data_size <= sizeof(T)); - - if (row > size()) - { - impl::throw_invalid("Invalid row index"); - } - - uint8_t const* ptr = m_data + row * m_row_size + m_columns[column].offset; - switch (data_size) - { - case 1: - { - uint8_t temp = *ptr; - return static_cast(temp); - } - case 2: - { - uint16_t temp = *reinterpret_cast(ptr); - return static_cast(temp); - } - case 4: - { - uint32_t temp = *reinterpret_cast(ptr); - return static_cast(temp); - } - default: - { - uint64_t temp = *reinterpret_cast(ptr); - return static_cast(temp); - } - } - } - - private: - - friend database; - - struct column - { - uint8_t offset; - uint8_t size; - }; - - database const* m_database; - uint8_t const* m_data{}; - uint32_t m_row_count{}; - uint8_t m_row_size{}; - std::array m_columns{}; - - void set_row_count(uint32_t const row_count) noexcept - { - XLANG_ASSERT(!m_row_count); - m_row_count = row_count; - } - - void set_columns(uint8_t const a, uint8_t const b = 0, uint8_t const c = 0, uint8_t const d = 0, uint8_t const e = 0, uint8_t const f = 0) noexcept - { - XLANG_ASSERT(a); - XLANG_ASSERT(a <= 8); - XLANG_ASSERT(b <= 8); - XLANG_ASSERT(c <= 8); - XLANG_ASSERT(d <= 8); - XLANG_ASSERT(e <= 8); - XLANG_ASSERT(f <= 8); - - XLANG_ASSERT(!m_row_size); - m_row_size = a + b + c + d + e + f; - XLANG_ASSERT(m_row_size < UINT8_MAX); - - m_columns[0] = { 0, a }; - if (b) { m_columns[1] = { static_cast(a), b }; } - if (c) { m_columns[2] = { static_cast(a + b), c }; } - if (d) { m_columns[3] = { static_cast(a + b + c), d }; } - if (e) { m_columns[4] = { static_cast(a + b + c + d), e }; } - if (f) { m_columns[5] = { static_cast(a + b + c + d + e), f }; } - } - - void set_data(byte_view& view) noexcept - { - XLANG_ASSERT(!m_data); - - if (m_row_count) - { - XLANG_ASSERT(m_row_size); - m_data = view.begin(); - view = view.seek(m_row_count * m_row_size); - } - } - - uint8_t index_size() const noexcept - { - return m_row_count < (1 << 16) ? 2 : 4; - } - }; - - template - struct index_base - { - index_base() noexcept = default; - - index_base(table_base const* const table, T const type, uint32_t const row) noexcept : - m_table{ table }, - m_value{ ((row + 1) << coded_index_bits_v) | static_cast(type) } - { - } - - index_base(table_base const* const table, uint32_t const value) noexcept : - m_table{ table }, - m_value{ value } - { - } - - explicit operator bool() const noexcept - { - return m_value != 0; - } - - T type() const noexcept - { - return static_cast(m_value & ((1 << coded_index_bits_v) - 1)); - } - - uint32_t index() const noexcept - { - return (m_value >> coded_index_bits_v) - 1; - } - - template - Row get_row() const; - - bool operator==(index_base const& other) const noexcept - { - return m_value == other.m_value; - } - - bool operator!=(index_base const& other) const noexcept - { - return !(*this == other); - } - - bool operator<(index_base const& other) const noexcept - { - return m_value < other.m_value; - } - - database const& get_database() const noexcept - { - return m_table->get_database(); - } - - protected: - - table_base const* m_table{}; - uint32_t m_value{}; - }; - - template - struct typed_index : index_base - { - using index_base::index_base; - }; - - template <> struct typed_index : index_base - { - using index_base::index_base; - - auto TypeRef() const; - auto TypeDef() const; - }; - - template - struct coded_index : typed_index - { - coded_index() noexcept = default; - - coded_index(table_base const* const table, T const type, uint32_t const row) noexcept : - typed_index{ table, type, row } - { - } - - coded_index(table_base const* const table, uint32_t const value) noexcept : - typed_index{ table, value } - { - } - }; - - template - struct row_base - { - using iterator_category = std::random_access_iterator_tag; - using value_type = Row; - using difference_type = int32_t; - using pointer = value_type*; - using reference = value_type&; - using const_reference = value_type const&; - - row_base(table_base const* const table, uint32_t const index) noexcept : m_table(table), m_index(index) - { - } - - uint32_t index() const noexcept - { - return m_index; - } - - template - auto coded_index() const noexcept - { - return reader::coded_index{ m_table, index_tag_v, index() }; - } - - template - T get_value(uint32_t const column) const - { - XLANG_ASSERT(*this); - return m_table->get_value(m_index, column); - } - - template - auto get_list(uint32_t const column) const; - - template - auto get_target_row(uint32_t const column) const; - - template - auto get_parent_row() const; - - database const& get_database() const noexcept - { - return m_table->get_database(); - } - - cache const& get_cache() const noexcept - { - return get_database().get_cache(); - } - - reference operator++() noexcept - { - ++m_index; - return static_cast(*this); - } - - value_type operator++(int) noexcept - { - row_base temp{ *this }; - operator++(); - return temp; - } - - reference operator--() noexcept - { - --m_index; - return static_cast(*this); - } - - value_type operator--(int) noexcept - { - row_base temp{ *this }; - operator--(); - return temp; - } - - reference operator+=(difference_type offset) noexcept - { - m_index += offset; - return static_cast(*this); - } - - value_type operator+(difference_type offset) const noexcept - { - return { m_table, m_index + offset }; - } - - reference operator-=(difference_type offset) noexcept - { - return *this += -offset; - } - - value_type operator-(difference_type offset) const noexcept - { - return *this + -offset; - } - - difference_type operator-(row_base const& other) const noexcept - { - XLANG_ASSERT(m_table == other.m_table); - return m_index - other.m_index; - } - - value_type operator[](difference_type offset) const noexcept - { - return { m_table, m_index + offset }; - } - - bool operator==(row_base const& other) const noexcept - { - return (m_table == other.m_table) && (m_index == other.m_index); - } - - bool operator!=(row_base const& other) const noexcept - { - return !(*this == other); - } - - bool operator<(row_base const& other) const noexcept - { - return m_table < other.m_table || (!(other.m_table < m_table) && m_index < other.m_index); - } - - bool operator>(row_base const& other) const noexcept - { - return other < *this; - } - - bool operator<=(row_base const& other) const noexcept - { - return !(other < *this); - } - - bool operator>=(row_base const& other) const noexcept - { - return !(*this < other); - } - - [[nodiscard]] const_reference operator*() const noexcept - { - return static_cast(*this); - } - - explicit operator bool() const noexcept - { - return m_table != nullptr; - } - - protected: - - row_base() noexcept = default; - - std::string_view get_string(uint32_t const column) const; - byte_view get_blob(uint32_t const column) const; - - template - auto get_coded_index(uint32_t const column) const - { - return reader::coded_index{ m_table, m_table->get_value(m_index, column) }; - } - - table_base const* get_table() const noexcept - { - return m_table; - } - - private: - - table_base const* m_table{}; - uint32_t m_index{}; - }; - - template - struct table : table_base - { - explicit table(database const* const database) noexcept : table_base{ database } - { - } - - T begin() const noexcept - { - return { this, 0 }; - } - - T end() const noexcept - { - return { this, size() }; - } - - T operator[](uint32_t const row) const noexcept - { - return { this, row }; - } - }; -} diff --git a/natvis/winmd/impl/winmd_reader/type_helpers.h b/natvis/winmd/impl/winmd_reader/type_helpers.h deleted file mode 100644 index 0d891bb7f..000000000 --- a/natvis/winmd/impl/winmd_reader/type_helpers.h +++ /dev/null @@ -1,43 +0,0 @@ - -namespace winmd::reader -{ - inline std::pair get_type_namespace_and_name(coded_index const& type) - { - if (type.type() == TypeDefOrRef::TypeDef) - { - auto const def = type.TypeDef(); - return { def.TypeNamespace(), def.TypeName() }; - } - else if (type.type() == TypeDefOrRef::TypeRef) - { - auto const ref = type.TypeRef(); - return { ref.TypeNamespace(), ref.TypeName() }; - } - else - { - XLANG_ASSERT(false); - return {}; - } - } - - inline std::pair get_base_class_namespace_and_name(TypeDef const& type) - { - return get_type_namespace_and_name(type.Extends()); - } - - inline auto extends_type(TypeDef type, std::string_view typeNamespace, std::string_view typeName) - { - return get_base_class_namespace_and_name(type) == std::pair(typeNamespace, typeName); - } - - inline bool is_nested(TypeDef const& type) - { - const auto visibility = type.Flags().Visibility(); - return !(visibility == TypeVisibility::Public || visibility == TypeVisibility::NotPublic); - } - - inline bool is_nested(TypeRef const& type) - { - return type.ResolutionScope().type() == ResolutionScope::TypeRef; - } -} diff --git a/natvis/winmd/impl/winmd_reader/view.h b/natvis/winmd/impl/winmd_reader/view.h deleted file mode 100644 index 91d841423..000000000 --- a/natvis/winmd/impl/winmd_reader/view.h +++ /dev/null @@ -1,308 +0,0 @@ - -namespace winmd::reader -{ - template - auto const& begin(std::pair const& values) noexcept - { - return values.first; - } - - template - auto const& end(std::pair const& values) noexcept - { - return values.second; - } - - template - auto distance(std::pair const& values) noexcept - { - return values.second - values.first; - } - - template - auto equal_range(Container const& container, T const& value) noexcept - { - return std::equal_range(container.begin(), container.end(), value); - } - - template - auto equal_range(Container const& container, T const& value, Compare compare) noexcept - { - return std::equal_range(container.begin(), container.end(), value, compare); - } - - struct byte_view; - inline int32_t uncompress_signed(byte_view& cursor, uint32_t length); - - struct byte_view - { - byte_view() noexcept = default; - byte_view(byte_view const&) noexcept = default; - byte_view& operator=(byte_view const&) noexcept = default; - - byte_view(byte_view&& other) noexcept : - m_first(std::exchange(other.m_first, {})), - m_last(std::exchange(other.m_last, {})) - { - } - - byte_view& operator=(byte_view&& other) noexcept - { - m_first = std::exchange(other.m_first, {}); - m_last = std::exchange(other.m_last, {}); - return *this; - } - - byte_view(uint8_t const* const first, uint8_t const* const last) noexcept : - m_first(first), - m_last(last) - { - } - - auto begin() const noexcept - { - return m_first; - } - - auto end() const noexcept - { - return m_last; - } - - uint32_t size() const noexcept - { - return static_cast(end() - begin()); - } - - explicit operator bool() const noexcept - { - return size() > 0; - } - - byte_view seek(uint32_t const offset) const - { - check_available(offset); - return{ m_first + offset, m_last }; - } - - byte_view sub(uint32_t const offset, uint32_t const size) const - { - check_available(offset + size); - return{ m_first + offset, m_first + offset + size }; - } - - template - T const& as(uint32_t const offset = 0) const - { - check_available(offset + sizeof(T)); - return reinterpret_cast(*(m_first + offset)); - } - - std::string_view as_string(uint32_t const offset = 0) const - { - static_assert(sizeof(uint8_t) == 1); - check_available(offset + 1); - auto const length = as(offset); - if (length == 0) - { - return ""; - } - else if (length == 0xff) - { - return { nullptr, 0 }; - } - else - { - check_available(offset + 1 + length); - return { reinterpret_cast(m_first + offset + 1), length }; - } - } - - 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 - { - check_available(offset + count * sizeof(T)); - return reinterpret_cast(m_first + offset); - } - - private: - - void check_available(uint32_t const offset) const - { - if (m_first + offset > m_last) - { - impl::throw_invalid("Buffer too small"); - } - } - - uint8_t const* m_first{}; - uint8_t const* m_last{}; - }; - - struct file_view : byte_view - { - file_view(file_view const&) = delete; - file_view& operator=(file_view const&) = delete; - file_view(file_view&&) noexcept = default; - file_view& operator=(file_view&&) noexcept = default; - - file_view(std::string_view const& path) : byte_view{ open_file(path) }, m_backed_by_file{ true } - { - } - - file_view(uint8_t const* const first, uint8_t const* const last) noexcept : byte_view{ first, last }, m_backed_by_file{ false } - { - } - - ~file_view() noexcept - { - if (m_backed_by_file) - { -#if defined(_WIN32) - UnmapViewOfFile(begin()); -#else - munmap(const_cast(reinterpret_cast(begin())), size()); -#endif - } - } - - private: - - bool m_backed_by_file; - -#if defined(_WIN32) - struct handle - { - HANDLE value{}; - - ~handle() noexcept - { - if (value) - { - CloseHandle(value); - } - } - - explicit operator bool() const noexcept - { - return value != 0; - } - }; -#endif - - struct file_handle - { -#if defined(_WIN32) - using handle_type = HANDLE; -#else - using handle_type = int; - static constexpr handle_type INVALID_HANDLE_VALUE = -1; -#endif - - handle_type value{ INVALID_HANDLE_VALUE }; - - ~file_handle() noexcept - { - if (value != INVALID_HANDLE_VALUE) - { -#if defined(_WIN32) - CloseHandle(value); -#else - close(value); -#endif - } - } - - explicit operator bool() const noexcept - { - return value != INVALID_HANDLE_VALUE; - } - }; - - static byte_view open_file(std::string_view const& path) - { -#if defined(_WIN32) - auto input = impl::c_str(path); - - auto const input_length = static_cast(path.length() + 1); - int buffer_length = MultiByteToWideChar(CP_UTF8, 0, input, input_length, 0, 0); - std::vector output = std::vector(buffer_length); - int result = MultiByteToWideChar(CP_UTF8, 0, input, input_length, output.data(), buffer_length); - - if (result == 0) - { - switch (GetLastError()) - { - case ERROR_INSUFFICIENT_BUFFER: - impl::throw_invalid("Insufficient buffer size"); - case ERROR_NO_UNICODE_TRANSLATION: - impl::throw_invalid("Untranslatable path"); - default: - impl::throw_invalid("Could not convert path"); - } - } - - file_handle file{ CreateFile2(output.data(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr) }; - - if (!file) - { - impl::throw_invalid("Could not open file '", path, "'"); - } - - LARGE_INTEGER size{}; - GetFileSizeEx(file.value, &size); - - if (!size.QuadPart) - { - return{}; - } - - handle mapping{ CreateFileMappingW(file.value, nullptr, PAGE_READONLY, 0, 0, nullptr) }; - - if (!mapping) - { - impl::throw_invalid("Could not open file '", path, "'"); - } - - auto const first{ static_cast(MapViewOfFile(mapping.value, FILE_MAP_READ, 0, 0, 0)) }; - return{ first, first + size.QuadPart }; -#else - file_handle file{ open(impl::c_str(path), O_RDONLY, 0) }; - if (!file) - { - impl::throw_invalid("Could not open file '", path, "'"); - } - - struct stat st; - int ret = fstat(file.value, &st); - if (ret < 0) - { - impl::throw_invalid("Could not open file '", path, "'"); - } - if (!st.st_size) - { - return{}; - } - -#if defined(__linux__) - auto const flags = MAP_PRIVATE | MAP_POPULATE; -#else - auto const flags = MAP_PRIVATE; -#endif - - auto const first = static_cast(mmap(nullptr, st.st_size, PROT_READ, flags, file.value, 0)); - if (first == MAP_FAILED) - { - impl::throw_invalid("Could not open file '", path, "'"); - } - - return{ first, first + st.st_size }; -#endif - } - }; -} diff --git a/natvis/winmd/winmd_reader.h b/natvis/winmd/winmd_reader.h deleted file mode 100644 index 4268abb04..000000000 --- a/natvis/winmd/winmd_reader.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "impl/base.h" -#include "impl/winmd_reader/pe.h" -#include "impl/winmd_reader/view.h" -#include "impl/winmd_reader/enum.h" -#include "impl/winmd_reader/enum_traits.h" -#include "impl/winmd_reader/flags.h" -#include "impl/winmd_reader/table.h" -#include "impl/winmd_reader/index.h" -#include "impl/winmd_reader/signature.h" -#include "impl/winmd_reader/schema.h" -#include "impl/winmd_reader/database.h" -#include "impl/winmd_reader/column.h" -#include "impl/winmd_reader/type_helpers.h" -#include "impl/winmd_reader/key.h" -#include "impl/winmd_reader/cache.h" -#include "impl/winmd_reader/filter.h" -#include "impl/winmd_reader/custom_attribute.h" -#include "impl/winmd_reader/helpers.h" From 333b7cb69535cca022103b7a2477c6100ebc38da Mon Sep 17 00:00:00 2001 From: Ryan Shepherd Date: Fri, 31 Jan 2025 12:55:08 -0800 Subject: [PATCH 12/16] No need for natvis namespace now that db was renamed to db_cache --- natvis/cppwinrt_visualizer.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/natvis/cppwinrt_visualizer.cpp b/natvis/cppwinrt_visualizer.cpp index 7da07f468..e22bb7fda 100644 --- a/natvis/cppwinrt_visualizer.cpp +++ b/natvis/cppwinrt_visualizer.cpp @@ -11,7 +11,7 @@ using namespace std::filesystem; using namespace winrt; using namespace winmd::reader; -namespace natvis +namespace { std::vector db_files; std::unique_ptr db_cache; @@ -106,10 +106,10 @@ void LoadMetadata(DkmProcess* process, WCHAR const* processPath, std::string_vie auto const path_string = winmd_path.string(); - if (std::find(natvis::db_files.begin(), natvis::db_files.end(), path_string) == natvis::db_files.end()) + if (std::find(db_files.begin(), db_files.end(), path_string) == db_files.end()) { - natvis::db_cache->add_database(path_string, [](TypeDef const& type) { return type.Flags().WindowsRuntime(); }); - natvis::db_files.push_back(path_string); + db_cache->add_database(path_string, [](TypeDef const& type) { return type.Flags().WindowsRuntime(); }); + db_files.push_back(path_string); } } auto pos = probe_file.rfind('.'); @@ -124,12 +124,12 @@ void LoadMetadata(DkmProcess* process, WCHAR const* processPath, std::string_vie TypeDef FindSimpleType(DkmProcess* process, std::string_view const& typeName) { XLANG_ASSERT(typeName.find('<') == std::string_view::npos); - auto type = natvis::db_cache->find(typeName); + auto type = db_cache->find(typeName); if (!type) { auto processPath = process->Path()->Value(); LoadMetadata(process, processPath, typeName); - type = natvis::db_cache->find(typeName); + type = db_cache->find(typeName); if (!type) { NatvisDiagnostic(process, @@ -142,7 +142,7 @@ TypeDef FindSimpleType(DkmProcess* process, std::string_view const& typeName) TypeDef FindSimpleType(DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName) { XLANG_ASSERT(typeName.find('<') == std::string_view::npos); - auto type = natvis::db_cache->find(typeNamespace, typeName); + auto type = db_cache->find(typeNamespace, typeName); if (!type) { std::string fullName(typeNamespace); @@ -245,10 +245,10 @@ cppwinrt_visualizer::cppwinrt_visualizer() { if (std::filesystem::is_regular_file(file)) { - natvis::db_files.push_back(file.path().string()); + db_files.push_back(file.path().string()); } } - natvis::db_cache.reset(new cache(natvis::db_files, [](TypeDef const& type) { return type.Flags().WindowsRuntime(); })); + db_cache.reset(new cache(db_files, [](TypeDef const& type) { return type.Flags().WindowsRuntime(); })); } catch (...) { @@ -270,8 +270,8 @@ cppwinrt_visualizer::cppwinrt_visualizer() cppwinrt_visualizer::~cppwinrt_visualizer() { ClearTypeResolver(); - natvis::db_files.clear(); - natvis::db_cache.reset(); + db_files.clear(); + db_cache.reset(); } HRESULT cppwinrt_visualizer::EvaluateVisualizedExpression( From 9855edf1c377e2147af9071f8567f3f077f5aa49 Mon Sep 17 00:00:00 2001 From: Ryan Shepherd Date: Fri, 31 Jan 2025 14:21:18 -0800 Subject: [PATCH 13/16] npos --- natvis/cppwinrt_visualizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/natvis/cppwinrt_visualizer.cpp b/natvis/cppwinrt_visualizer.cpp index e22bb7fda..bffbde22c 100644 --- a/natvis/cppwinrt_visualizer.cpp +++ b/natvis/cppwinrt_visualizer.cpp @@ -196,7 +196,7 @@ TypeSig ResolveGenericTypePart(DkmProcess* process, iter& it, sent const& end) TypeDef type = FindSimpleType(process, partName); auto tickPos = partName.rfind('`'); - if (tickPos == std::string_view::basic_string_view::npos) + if (tickPos == partName.npos) { return TypeSig{ type.coded_index() }; } From 45c7daf70f81fd6bea9d2d88da4f29656a4dcee8 Mon Sep 17 00:00:00 2001 From: Ryan Shepherd Date: Fri, 31 Jan 2025 16:44:27 -0800 Subject: [PATCH 14/16] Handle Char16 in generic params --- natvis/cppwinrt_visualizer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/natvis/cppwinrt_visualizer.cpp b/natvis/cppwinrt_visualizer.cpp index bffbde22c..271d5fd2c 100644 --- a/natvis/cppwinrt_visualizer.cpp +++ b/natvis/cppwinrt_visualizer.cpp @@ -185,6 +185,7 @@ TypeSig ResolveGenericTypePart(DkmProcess* process, iter& it, sent const& end) {"Single", ElementType::R4}, {"Double", ElementType::R8}, {"String", ElementType::String}, + {"Char16", ElementType::Char}, {"Object", ElementType::Object} }; std::string_view partName = *it; From 3ca09400f0415a399a3226f5dab14f84c6f212dc Mon Sep 17 00:00:00 2001 From: Ryan Shepherd Date: Fri, 31 Jan 2025 17:40:17 -0800 Subject: [PATCH 15/16] Enable Guid in generic type param --- natvis/cppwinrt_visualizer.cpp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/natvis/cppwinrt_visualizer.cpp b/natvis/cppwinrt_visualizer.cpp index 271d5fd2c..3f3daab6f 100644 --- a/natvis/cppwinrt_visualizer.cpp +++ b/natvis/cppwinrt_visualizer.cpp @@ -15,6 +15,34 @@ namespace { std::vector db_files; std::unique_ptr db_cache; + coded_index guid_TypeRef{}; +} + +coded_index FindGuidType() +{ + if (!guid_TypeRef) + { + // There is no definitive TypeDef for System.Guid. But there are a variety of TypeRefs scattered about + // This one should be relatively quick to find + auto pv = db_cache->find("Windows.Foundation", "PropertyValue"); + for (auto&& method : pv.MethodList()) + { + if (method.Name() == "CreateGuid") + { + auto const& sig = method.Signature(); + auto const& params = sig.Params(); + if (params.first != params.second) + { + auto const& type = params.first->Type().Type(); + if (std::holds_alternative>(type)) + { + guid_TypeRef = std::get>(type); + } + } + } + } + } + return guid_TypeRef; } void MetadataDiagnostic(DkmProcess* process, std::wstring const& status, std::filesystem::path const& path) @@ -194,6 +222,11 @@ TypeSig ResolveGenericTypePart(DkmProcess* process, iter& it, sent const& end) { return TypeSig{ basic_type_pos->second }; } + + if (partName == "Guid") + { + return TypeSig{ FindGuidType() }; + } TypeDef type = FindSimpleType(process, partName); auto tickPos = partName.rfind('`'); @@ -271,6 +304,7 @@ cppwinrt_visualizer::cppwinrt_visualizer() cppwinrt_visualizer::~cppwinrt_visualizer() { ClearTypeResolver(); + guid_TypeRef = {}; db_files.clear(); db_cache.reset(); } From e3c5189000027327fa3d53992dc05c4811a7bf13 Mon Sep 17 00:00:00 2001 From: Ryan Shepherd Date: Wed, 5 Feb 2025 11:43:34 -0800 Subject: [PATCH 16/16] Use IPropertyValue.GetGuid() instead of PropertyValue.CreateGuid() --- natvis/cppwinrt_visualizer.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/natvis/cppwinrt_visualizer.cpp b/natvis/cppwinrt_visualizer.cpp index 3f3daab6f..4b0a0c370 100644 --- a/natvis/cppwinrt_visualizer.cpp +++ b/natvis/cppwinrt_visualizer.cpp @@ -24,20 +24,20 @@ coded_index FindGuidType() { // There is no definitive TypeDef for System.Guid. But there are a variety of TypeRefs scattered about // This one should be relatively quick to find - auto pv = db_cache->find("Windows.Foundation", "PropertyValue"); + auto pv = db_cache->find("Windows.Foundation", "IPropertyValue"); for (auto&& method : pv.MethodList()) { - if (method.Name() == "CreateGuid") + if (method.Name() == "GetGuid") { auto const& sig = method.Signature(); - auto const& params = sig.Params(); - if (params.first != params.second) + auto const& type = sig.ReturnType().Type().Type(); + XLANG_ASSERT(std::holds_alternative>(type)); + if (std::holds_alternative>(type)) { - auto const& type = params.first->Type().Type(); - if (std::holds_alternative>(type)) - { - guid_TypeRef = std::get>(type); - } + guid_TypeRef = std::get>(type); + XLANG_ASSERT(guid_TypeRef.type() == TypeDefOrRef::TypeRef); + XLANG_ASSERT(guid_TypeRef.TypeRef().TypeNamespace() == "System"); + XLANG_ASSERT(guid_TypeRef.TypeRef().TypeName() == "Guid"); } } }