diff --git a/natvis/cppwinrt_visualizer.cpp b/natvis/cppwinrt_visualizer.cpp index 7aa845e53..e22bb7fda 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 +{ + std::vector db_files; + std::unique_ptr db_cache; +} void MetadataDiagnostic(DkmProcess* process, std::wstring const& status, std::filesystem::path const& path) { @@ -118,8 +121,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,19 +139,98 @@ 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) { std::string fullName(typeNamespace); fullName.append("."); fullName.append(typeName); - FindType(process, fullName); + FindSimpleType(process, fullName); } return type; } +std::vector ParseTypeName(std::string_view name) +{ + 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) + { + result.push_back(winrt::to_string(part)); + } + 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) + { + 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 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, typeName).coded_index() }; + } + else + { + return ResolveGenericType(process, typeName); + } +} + cppwinrt_visualizer::cppwinrt_visualizer() { try @@ -193,7 +276,7 @@ cppwinrt_visualizer::~cppwinrt_visualizer() HRESULT cppwinrt_visualizer::EvaluateVisualizedExpression( _In_ DkmVisualizedExpression* pVisualizedExpression, - _Deref_out_ DkmEvaluationResult** ppResultObject + _COM_Outptr_result_maybenull_ DkmEvaluationResult** ppResultObject ) { try @@ -233,6 +316,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 - + Debug @@ -321,6 +321,6 @@ - + \ No newline at end of file diff --git a/natvis/object_visualizer.cpp b/natvis/object_visualizer.cpp index a0803871a..7f99aab97 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; } @@ -207,6 +207,99 @@ static std::string GetRuntimeClass( return to_string(pValue->Value()); } +template +struct EvaulationResultConverter +{ + static constexpr auto category{ PropertyCategory::Class }; +}; + +template <> +struct EvaulationResultConverter +{ + static constexpr auto category{ PropertyCategory::Bool }; + static void Convert(_In_ DkmString* pValue, _In_ DkmString* pType, _In_opt_ DkmDataAddress*, _Out_ bool& result) + { + if (pType->Value() != L"bool"sv) + { + throw winrt::hresult_error(E_FAIL, std::wstring(L"Evaluation returned incorrect type: ") + pType->Value()); + } + if (pValue->Value() == L"true"sv) + { + result = true; + } + else if (pValue->Value() == L"false"sv) + { + result = false; + } + else + { + throw hresult_out_of_bounds(std::wstring(L"result out of range: ") + pValue->Value()); + } + } +}; + +template <> +struct EvaulationResultConverter +{ + static constexpr auto category{ PropertyCategory::Uint4 }; + static void Convert(_In_ DkmString* pValue, _In_ DkmString* pType, _In_opt_ DkmDataAddress*, _Out_ uint32_t& result) + { + if (pType->Value() != L"uint32_t"sv && pType->Value() != L"unsigned int"sv) + { + throw winrt::hresult_error(E_FAIL, std::wstring(L"Evaluation returned incorrect type: ") + pType->Value()); + } + result = std::stoul(std::wstring(pValue->Value()), nullptr, 0); + } +}; + +template <> +struct EvaulationResultConverter> +{ + static constexpr auto category{ PropertyCategory::Class }; + static void Convert(_In_ DkmString*, _In_ DkmString*, _In_opt_ DkmDataAddress* pAddress, _Out_ com_ptr& result) + { + com_ptr pointer; + winrt::check_hresult(DkmPointerValueHome::Create(pAddress->Value(), result.put())); + } +}; + +template +static HRESULT EvaluatePropertyValue( + const PropertyData& prop, + _In_ DkmVisualizedExpression* pExpression, + _In_ DkmPointerValueHome* pObject, + ObjectType objectType, + _Out_ T& value +) try +{ + XLANG_ASSERT(prop.category == EvaulationResultConverter::category); + com_ptr pEvaluationResult; + winrt::check_hresult(EvaluatePropertyExpression(prop, pExpression, pObject, objectType, pEvaluationResult)); + auto pSuccessEvaluationResult = DkmSuccessEvaluationResult::TryCast(pEvaluationResult.get()); + if (!pSuccessEvaluationResult) + { + std::wstring errorMessage = L"Evaluation failed: "; + if (auto pFailedEvaluationResult = DkmFailedEvaluationResult::TryCast(pEvaluationResult.get())) + { + errorMessage += pFailedEvaluationResult->ErrorMessage()->Value(); + } + throw winrt::hresult_error(E_FAIL, errorMessage); + } + auto pValue = pSuccessEvaluationResult->Value(); + auto pType = pSuccessEvaluationResult->Type(); + if (!pValue || !pType) + { + throw winrt::hresult_error(E_FAIL, L"Evaluation returned null value or type"); + } + EvaulationResultConverter::Convert(pValue, pType, pSuccessEvaluationResult->Address(), value); + return S_OK; +} +catch (...) +{ + NatvisDiagnostic(pExpression, std::wstring(L"EvaluatePropertyValue error - ") + winrt::to_message(), NatvisDiagnosticLevel::Error); + return winrt::to_hresult(); +} + static HRESULT ObjectToString( _In_ DkmVisualizedExpression* pExpression, _In_ DkmPointerValueHome* pObject, @@ -238,6 +331,7 @@ static HRESULT ObjectToString( static HRESULT CreateChildVisualizedExpression( _In_ PropertyData const& prop, _In_ DkmVisualizedExpression* pParent, + _In_ DkmPointerValueHome* pObject, ObjectType objectType, _Deref_out_ DkmChildVisualizedExpression** ppResult ) @@ -245,9 +339,7 @@ static HRESULT CreateChildVisualizedExpression( *ppResult = nullptr; com_ptr pEvaluationResult; - auto valueHome = make_com_ptr(pParent->ValueHome()); - com_ptr pParentPointer = valueHome.as(); - IF_FAIL_RET(EvaluatePropertyExpression(prop, pParent, pParentPointer.get(), objectType, pEvaluationResult)); + IF_FAIL_RET(EvaluatePropertyExpression(prop, pParent, pObject, objectType, pEvaluationResult)); if (pEvaluationResult->TagValue() != DkmEvaluationResult::Tag::SuccessResult) { return E_FAIL; @@ -278,7 +370,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; } @@ -340,19 +432,571 @@ static HRESULT CreateChildVisualizedExpression( return S_OK; } -struct property_type +static HRESULT CreateChildVisualizedExpression( + _In_ PropertyData const& prop, + _In_ DkmVisualizedExpression* pParent, + ObjectType objectType, + _Deref_out_ DkmChildVisualizedExpression** ppResult +) +{ + auto pParentPointer = DkmPointerValueHome::TryCast(pParent->ValueHome()); + if (!pParentPointer) + { + return E_NOINTERFACE; + } + return CreateChildVisualizedExpression(prop, pParent, pParentPointer, objectType, ppResult); +} + +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 { - MethodDef get; - MethodDef set; + 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; + write("<"); + for (auto&& elem : type.GenericArgs()) + { + if (first) + { + first = false; + } + else + { + write(", "); + } + write(elem); + } + write(">"); + } + + 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()); + } }; +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 (auto pGenericInst = std::get_if(&type.Type())) + { + 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 }; +} + +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; +} + +std::optional CreatePropertyData( + Microsoft::VisualStudio::Debugger::DkmProcess* process, + TypeSig const& typeSig, + MethodDef const& method, + std::string_view propName, + const std::wstring& propIid, + int32_t propIndex +) +{ + std::optional propCategory = GetPropertyCategory(process, typeSig, method.Signature().ReturnType().Type()); + if (propCategory) + { + std::wstring propAbiType; + std::wstring propDisplayType; + if (!IsBuiltIn(*propCategory)) + { + writer writer; + if (auto pGenericTypeInst = std::get_if(&typeSig.Type())) + { + auto const& genericArgs = pGenericTypeInst->GenericArgs(); + writer.generic_params.assign(genericArgs.first, genericArgs.second); + } + writer.write(method.Signature().ReturnType().Type()); + propDisplayType = string_to_wstring(writer.result); + + if (*propCategory == PropertyCategory::Class) + { + propAbiType = L"winrt::impl::inspectable_abi*"; + } + else + { + propAbiType = propDisplayType; + } + } + + return PropertyData(propIid, propIndex, *propCategory, std::move(propAbiType), std::move(propDisplayType), string_to_wstring(propName)); + } + return {}; +} + +template +MethodDef FindMethodByName(MethodList&& methods, std::string_view const& name) +{ + auto iter = std::ranges::find_if(methods, [&name](auto&& method) { return name == method.Name(); }); + if (iter != end(methods)) + { + return *iter; + } + else + { + return MethodDef{ nullptr, 0 }; + } +} + +// Return property names to ignore in normal processing +std::vector GetIteratorPseudoProperties( + Microsoft::VisualStudio::Debugger::DkmProcess* process, + TypeSig const& typeSig, + TypeDef const& type, + std::wstring const& propIid, + _Inout_ PseudoPropertyData& pseudoPropertyData) +{ + if ((type.TypeNamespace() != "Windows.Foundation.Collections"sv) || + type.TypeName() != "IIterator`1"sv) + { + return {}; + } + + const auto currentMethod = FindMethodByName(type.MethodList(), "get_Current"sv); + if (!currentMethod || !currentMethod.Flags().SpecialName()) + { + NatvisDiagnostic(process, L"IIterator.Current not present or missing SpecialName flag", NatvisDiagnosticLevel::Warning); + return {}; + } + + const auto hasCurrentMethod = FindMethodByName(type.MethodList(), "get_HasCurrent"sv); + if (!hasCurrentMethod || !hasCurrentMethod.Flags().SpecialName()) + { + NatvisDiagnostic(process, L"IIterator.HasCurrent not present or missing SpecialName flag", NatvisDiagnosticLevel::Warning); + return {}; + } + + auto currentPropData = CreatePropertyData(process, typeSig, currentMethod, "Current", propIid, currentMethod - type.MethodList().first); + if (!currentPropData) + { + NatvisDiagnostic(process, L"Failed to create property data for IIterator.Current", NatvisDiagnosticLevel::Warning); + return {}; + } + + auto hasCurrentPropData = CreatePropertyData(process, typeSig, hasCurrentMethod, "HasCurrent", propIid, hasCurrentMethod - type.MethodList().first); + if (!hasCurrentPropData) + { + NatvisDiagnostic(process, L"Failed to create property data for IIterator.HasCurrent", NatvisDiagnosticLevel::Warning); + return {}; + } + + XLANG_ASSERT(!pseudoPropertyData.m_iteratorPropertyData); + pseudoPropertyData.m_iteratorPropertyData = std::make_unique(); + pseudoPropertyData.m_iteratorPropertyData->currentProperty = std::move(currentPropData).value(); + pseudoPropertyData.m_iteratorPropertyData->hasCurrentProperty = std::move(hasCurrentPropData).value(); + return { "get_Current"sv, "get_HasCurrent"sv }; +} + +// Return property names to ignore in normal processing +std::vector GetVectorPseudoProperties( + Microsoft::VisualStudio::Debugger::DkmProcess* process, + TypeSig const& typeSig, + TypeDef const& type, + std::wstring const& propIid, + _Inout_ PseudoPropertyData& pseudoPropertyData) +{ + if (type.TypeNamespace() != "Windows.Foundation.Collections"sv || + (type.TypeName() != "IVector`1"sv && type.TypeName() != "IVectorView`1"sv && type.TypeName() != "IIterable`1"sv)) + { + return {}; + } + + if (!pseudoPropertyData.m_vectorPropertyData) + { + pseudoPropertyData.m_vectorPropertyData = std::make_unique(); + } + + if (type.TypeName() == "IVector`1"sv || type.TypeName() == "IVectorView`1"sv) + { + const auto sizeMethod = FindMethodByName(type.MethodList(), "get_Size"sv); + if (!sizeMethod || !sizeMethod.Flags().SpecialName()) + { + NatvisDiagnostic(process, L"IVector.Size not present or missing SpecialName flag", NatvisDiagnosticLevel::Warning); + return {}; + } + auto propData = CreatePropertyData(process, typeSig, sizeMethod, "Size", propIid, sizeMethod - type.MethodList().first); + if (!propData) + { + NatvisDiagnostic(process, L"Failed to create property data for IVector.Size", NatvisDiagnosticLevel::Warning); + return {}; + } + pseudoPropertyData.m_vectorPropertyData->sizeProperty = std::move(propData).value(); + // It's OK to not ignore Size and leave it also as a normal property. + return {}; + } + + XLANG_ASSERT(type.TypeName() == "IIterable`1"sv); + const auto firstMethod = FindMethodByName(type.MethodList(), "First"sv); + if (!firstMethod) + { + NatvisDiagnostic(process, L"Failed to find method for IIterable.First()", NatvisDiagnosticLevel::Warning); + return {}; + } + auto firstProperty = CreatePropertyData(process, typeSig, firstMethod, "First", propIid, firstMethod - type.MethodList().first); + if (!firstProperty) + { + NatvisDiagnostic(process, L"Failed to create property data for IIterable.First()", NatvisDiagnosticLevel::Warning); + return {}; + } + + // Follow the return type of First() to get IIterator + auto firstReturnType = firstMethod.Signature().ReturnType().Type(); + auto firstReturnGenericInst = std::get_if(&firstReturnType.Type()); + if (!firstReturnGenericInst) + { + NatvisDiagnostic(process, L"IIterable.First() return type not a GenericTypeInst", NatvisDiagnosticLevel::Warning); + return {}; + } + auto iterableGenericInst = std::get_if(&typeSig.Type()); + if (!iterableGenericInst) + { + NatvisDiagnostic(process, L"IIterable not a GenericTypeInst", NatvisDiagnosticLevel::Warning); + return {}; + } + auto iterableGenericArgs = iterableGenericInst->GenericArgs(); + + // So far, we should have IIterator`1. Now we need to instantiate this with the generic arg in the originating IIterable` to get IIterator`1 + TypeSig iteratorTypeSig = TypeSig{ ReplaceGenericIndices(*firstReturnGenericInst, std::vector{ iterableGenericArgs.first, iterableGenericArgs.second }) }; + auto [iteratorType, iteratorPropIid] = ResolveTypeInterface(process, iteratorTypeSig); + + if (iteratorType.TypeNamespace() != "Windows.Foundation.Collections"sv || iteratorType.TypeName() != "IIterator`1"sv) + { + NatvisDiagnostic(process, L"IIterable.First() did not resolve to IIterator", NatvisDiagnosticLevel::Warning); + return {}; + } + + const auto moveNextMethod = FindMethodByName(iteratorType.MethodList(), "MoveNext"sv); + if (!moveNextMethod) + { + NatvisDiagnostic(process, L"Failed to find method for IIterator.MoveNext()", NatvisDiagnosticLevel::Warning); + return {}; + } + auto moveNextProperty = CreatePropertyData(process, iteratorTypeSig, moveNextMethod, "MoveNext", iteratorPropIid, moveNextMethod - iteratorType.MethodList().first); + if (!moveNextProperty) + { + NatvisDiagnostic(process, L"Failed to create property data for IIterator.MoveNext()", NatvisDiagnosticLevel::Warning); + return {}; + } + const auto currentMethod = FindMethodByName(iteratorType.MethodList(), "get_Current"sv); + if (!currentMethod) + { + NatvisDiagnostic(process, L"Failed to find method for IIterator.get_Current()", NatvisDiagnosticLevel::Warning); + return {}; + } + auto currentProperty = CreatePropertyData(process, iteratorTypeSig, currentMethod, "Current", iteratorPropIid, currentMethod - iteratorType.MethodList().first); + if (!currentProperty) + { + NatvisDiagnostic(process, L"Failed to create property data for IIterator.get_Current()", NatvisDiagnosticLevel::Warning); + return {}; + } + + + pseudoPropertyData.m_vectorPropertyData->firstProperty = std::move(firstProperty).value(); + pseudoPropertyData.m_vectorPropertyData->moveNextProperty = std::move(moveNextProperty).value(); + pseudoPropertyData.m_vectorPropertyData->currentProperty = std::move(currentProperty).value(); + return {}; +} + +// Return property names to ignore in normal processing +std::vector GetPseudoProperties( + Microsoft::VisualStudio::Debugger::DkmProcess* process, + TypeSig const& typeSig, + TypeDef const& type, + std::wstring const& propIid, + _Inout_ PseudoPropertyData& pseudoPropertyData) +{ + std::vector result; + auto ignore = GetIteratorPseudoProperties(process, typeSig, type, propIid, pseudoPropertyData); + result.insert(result.end(), ignore.begin(), ignore.end()); + ignore = GetVectorPseudoProperties(process, typeSig, type, propIid, pseudoPropertyData); + result.insert(result.end(), ignore.begin(), ignore.end()); + return result; +} + 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); + _Inout_ bool& isStringable, + _Inout_ PseudoPropertyData& pseudoPropertyData) +{ + auto [type, propIid] = ResolveTypeInterface(process, typeSig); + if (!type) { return; @@ -364,6 +1008,8 @@ void GetInterfaceData( return; } + auto ignoreMethods = GetPseudoProperties(process, typeSig, type, propIid, pseudoPropertyData); + int32_t propIndex = -1; for (auto&& method : type.MethodList()) { @@ -374,108 +1020,19 @@ void GetInterfaceData( { continue; } + if (std::ranges::any_of(ignoreMethods, [methodName = method.Name()](auto&& name) { return name == methodName; })) + { + 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) - { - auto type = ResolveType(process, index); - if (!type) - { - return; - } - - auto typeName = type.TypeName(); - if (typeName == "GUID"sv) - { - propCategory = PropertyCategory::Guid; - } - 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; - } - } - }, - [&](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) + std::string_view propName = method.Name().substr(4); + auto propData = CreatePropertyData(process, typeSig, method, propName, propIid, propIndex); + if (!propData.has_value()) { - auto propName = method.Name().substr(4); - std::wstring propDisplayName(propName.cbegin(), propName.cend()); - propertyData.push_back({ propIid, propIndex, *propCategory, propAbiType, propDisplayType, propDisplayName }); + continue; } + + propertyData.emplace_back(std::move(propData).value()); } } @@ -496,7 +1053,17 @@ void object_visualizer::GetPropertyData() 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 = ResolveType(process, *index); + } + else if (auto const* genericInst = std::get_if(&typeSig.Type())) + { + type = ResolveType(process, genericInst->GenericType()); + } + if (!type) { return; @@ -516,7 +1083,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, m_pseudoPropertyData); } } else if (get_category(type) == category::interface_type) @@ -524,9 +1091,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, m_pseudoPropertyData); } - GetInterfaceData(process, type.coded_index(), m_propertyData, m_isStringable); + GetInterfaceData(process, typeSig, m_propertyData, m_isStringable, m_pseudoPropertyData); } } @@ -622,8 +1189,10 @@ HRESULT object_visualizer::GetChildren( _Out_ DkmArray* pInitialChildren, _Deref_out_ DkmEvaluationResultEnumContext** ppEnumContext) { + pInitialChildren->Members = nullptr; + pInitialChildren->Length = 0; // Ignore metadata errors to ensure that Raw Data is always available - if (m_propertyData.empty()) + if (GetChildCount() == 0) { try { @@ -644,21 +1213,67 @@ HRESULT object_visualizer::GetChildren( } } + IF_FAIL_RET(EnsurePseudoProperties()); + com_ptr pEnumContext; IF_FAIL_RET(DkmEvaluationResultEnumContext::Create( - static_cast(m_propertyData.size()), + static_cast(GetChildCount()), m_pVisualizedExpression->StackFrame(), pInspectionContext, this, pEnumContext.put())); - IF_FAIL_RET(GetItems(m_pVisualizedExpression.get(), pEnumContext.get(), 0, InitialRequestSize, pInitialChildren)); + if (InitialRequestSize > 0) + { + IF_FAIL_RET(GetItems(m_pVisualizedExpression.get(), pEnumContext.get(), 0, InitialRequestSize, pInitialChildren)); + } *ppEnumContext = pEnumContext.detach(); return S_OK; } +HRESULT CreateFailedEvaulationExpression(_In_ DkmVisualizedExpression* pParent, + _In_ const DkmSourceString& errorMessage, + _In_ const DkmSourceString& displayName, + _COM_Outptr_ DkmChildVisualizedExpression** ppResult) +{ + com_ptr pErrorMessage; + IF_FAIL_RET(DkmString::Create(errorMessage, pErrorMessage.put())); + + com_ptr pDisplayName; + IF_FAIL_RET(DkmString::Create(displayName, pDisplayName.put())); + + com_ptr pVisualizedResult; + IF_FAIL_RET(DkmFailedEvaluationResult::Create( + pParent->InspectionContext(), + pParent->StackFrame(), + pDisplayName.get(), + nullptr, + pErrorMessage.get(), + DkmEvaluationResultFlags::ExceptionThrown, + DkmDataItem::Null(), + pVisualizedResult.put() + )); + + com_ptr pVisualizedExpression; + IF_FAIL_RET(DkmChildVisualizedExpression::Create( + pParent->InspectionContext(), + pParent->VisualizerId(), + pParent->SourceId(), + pParent->StackFrame(), + nullptr, + pVisualizedResult.get(), + pParent, + 2, + DkmDataItem::Null(), + pVisualizedExpression.put() + )); + + *ppResult = pVisualizedExpression.detach(); + return S_OK; +} + HRESULT object_visualizer::GetItems( _In_ DkmVisualizedExpression* pVisualizedExpression, _In_ DkmEvaluationResultEnumContext* /*pEnumContext*/, @@ -667,50 +1282,210 @@ HRESULT object_visualizer::GetItems( _Out_ DkmArray* pItems) { CAutoDkmArray resultValues; - IF_FAIL_RET(DkmAllocArray(std::min(m_propertyData.size(), size_t(Count)), &resultValues)); + auto childCount = std::min(GetChildCount() - StartIndex, (size_t)Count); + auto EndIndex = StartIndex + childCount; + + IF_FAIL_RET(DkmAllocArray(childCount, &resultValues)); auto pParent = pVisualizedExpression; - auto childCount = std::min(m_propertyData.size() - StartIndex, (size_t)Count); - for(size_t i = 0; i < childCount; ++i) + UINT32 currentIndex = StartIndex; + for(; currentIndex < m_propertyData.size() && currentIndex < EndIndex; ++currentIndex) { - auto& prop = m_propertyData[i + (size_t)StartIndex]; + auto& prop = m_propertyData[currentIndex]; com_ptr pPropertyVisualized; if(FAILED(CreateChildVisualizedExpression(prop, pParent, m_objectType, pPropertyVisualized.put()))) { - com_ptr pErrorMessage; - IF_FAIL_RET(DkmString::Create(L"", pErrorMessage.put())); - - com_ptr pDisplayName; - IF_FAIL_RET(DkmString::Create(prop.displayName.c_str(), pDisplayName.put())); - - com_ptr pVisualizedResult; - IF_FAIL_RET(DkmFailedEvaluationResult::Create( - pParent->InspectionContext(), - pParent->StackFrame(), - pDisplayName.get(), - nullptr, - pErrorMessage.get(), - DkmEvaluationResultFlags::ExceptionThrown, - DkmDataItem::Null(), - pVisualizedResult.put() - )); - - IF_FAIL_RET(DkmChildVisualizedExpression::Create( - pParent->InspectionContext(), - pParent->VisualizerId(), - pParent->SourceId(), - pParent->StackFrame(), - nullptr, - pVisualizedResult.get(), - pParent, - 2, - DkmDataItem::Null(), - pPropertyVisualized.put() - )); - } - resultValues.Members[i] = pPropertyVisualized.detach(); + IF_FAIL_RET(CreateFailedEvaulationExpression(pParent, L"", prop.displayName.c_str(), pPropertyVisualized.put())); + } + resultValues.Members[currentIndex - StartIndex] = pPropertyVisualized.detach(); } + auto propertiesAdded = currentIndex - StartIndex; + auto pseudoPropertyStartIndex = currentIndex - m_propertyData.size(); + IF_FAIL_RET(EvaluatePseudoProperties(pVisualizedExpression, pseudoPropertyStartIndex, Count - propertiesAdded, resultValues.Members + propertiesAdded)); + *pItems = resultValues.Detach(); return S_OK; } + +void EvaulateIteratorPseudoProperty( + _In_ Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pParent, + _In_ DkmPointerValueHome* pObject, + _In_ ObjectType objectType, + _Inout_ IteratorPropertyData& propertyData, + _COM_Outptr_ DkmChildVisualizedExpression** ppResult) +{ + if (!propertyData.hasCurrent.has_value()) + { + bool value; + if (FAILED(EvaluatePropertyValue(propertyData.hasCurrentProperty, pParent, pObject, objectType, value))) + { + check_hresult(CreateFailedEvaulationExpression(pParent, L"", propertyData.hasCurrentProperty.displayName.c_str(), ppResult)); + } + propertyData.hasCurrent = value; + } + + const auto& prop = propertyData.hasCurrent.value() ? propertyData.currentProperty : propertyData.hasCurrentProperty; + com_ptr pPropertyVisualized; + if (FAILED(CreateChildVisualizedExpression(prop, pParent, objectType, pPropertyVisualized.put()))) + { + check_hresult(CreateFailedEvaulationExpression(pParent, L"", prop.displayName.c_str(), ppResult)); + } + *ppResult = pPropertyVisualized.detach(); +} + +void EvaluateNextVectorElement( + _In_ Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pParent, + _Inout_ VectorPropertyData& propertyData) +{ + XLANG_ASSERT(propertyData.elementExpressions.size() < propertyData.size); + auto currentIndex = propertyData.elementExpressions.size(); + com_ptr pPropertyVisualized; + auto elementProperty = propertyData.currentProperty; + elementProperty.displayName = std::format(L"[{}]", currentIndex); + if (FAILED(CreateChildVisualizedExpression(elementProperty, pParent, propertyData.iteratorInstance.get(), ObjectType::Abi, pPropertyVisualized.put()))) + { + check_hresult(CreateFailedEvaulationExpression(pParent, L"", elementProperty.displayName.c_str(), pPropertyVisualized.put())); + } + propertyData.elementExpressions.push_back(std::move(pPropertyVisualized)); + + // Execute MoveCurrent + bool moveCurrentResult; + check_hresult(EvaluatePropertyValue(propertyData.moveNextProperty, pParent, propertyData.iteratorInstance.get(), ObjectType::Abi, moveCurrentResult)); +} + +size_t EvaluateVectorPseudoProperties( + _In_ Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pParent, + _In_ DkmPointerValueHome* /*pObject*/, + _In_ ObjectType /*objectType*/, + size_t StartIndex, + size_t Count, + _Inout_ VectorPropertyData& propertyData, + _Outptr_result_buffer_to_(Count, return) DkmChildVisualizedExpression** ppResult) +{ + if (StartIndex == Count) + { + return 0; + } + + if (propertyData.failureExpression) + { + propertyData.failureExpression.copy_to(ppResult); + return 1; + } + + // If this isn't the case, did we get out of sync somehow? + XLANG_ASSERT(propertyData.elementExpressions.size() == StartIndex); + + auto elementsToFetch = std::min(Count, propertyData.size.value() - propertyData.elementExpressions.size()); + for (size_t i = 0; i < elementsToFetch; ++i) + { + EvaluateNextVectorElement(pParent, propertyData); + propertyData.elementExpressions.back().copy_to(ppResult + i); + } + return elementsToFetch; +} + +HRESULT object_visualizer::EvaluatePseudoProperties( + _In_ Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pParent, + size_t StartIndex, + size_t Count, + _Outptr_result_buffer_(Count) Microsoft::VisualStudio::Debugger::Evaluation::DkmChildVisualizedExpression** expressions) try +{ + if (StartIndex == Count) + { + return S_OK; + } + + auto valueHome = make_com_ptr(pParent->ValueHome()); + auto pObject = DkmPointerValueHome::TryCast(valueHome.get()); + if (!pObject) + { + return E_NOINTERFACE; + } + + if (m_pseudoPropertyData.m_iteratorPropertyData) + { + if (StartIndex == 0) + { + EvaulateIteratorPseudoProperty(pParent, pObject, m_objectType, *m_pseudoPropertyData.m_iteratorPropertyData, expressions); + ++StartIndex; + ++expressions; + --Count; + } + --StartIndex; + } + + if (m_pseudoPropertyData.m_vectorPropertyData) + { + EvaluateVectorPseudoProperties(pParent, pObject, m_objectType, StartIndex, Count, *m_pseudoPropertyData.m_vectorPropertyData, expressions); + } + + return S_OK; +} +catch (...) +{ + return winrt::to_hresult(); +} + +HRESULT EnsureVectorPseudoProperties( + _In_ DkmVisualizedExpression* pParent, + _In_ DkmPointerValueHome* pParentObject, + ObjectType parentObjectType, + _Inout_ VectorPropertyData& propertyData) +{ + // Evaluate Size and obtain IITerator from IIterable.First() for vector properties + uint32_t size; + if (FAILED(EvaluatePropertyValue(propertyData.sizeProperty, pParent, pParentObject, parentObjectType, size))) + { + return CreateFailedEvaulationExpression(pParent, L"Failed to evaluate collection Size", L"[elements]", propertyData.failureExpression.put()); + } + com_ptr pIterator; + if (FAILED(EvaluatePropertyValue(propertyData.firstProperty, pParent, pParentObject, parentObjectType, pIterator))) + { + return CreateFailedEvaulationExpression(pParent, L"Failed to evaluate collection.First()", L"[elements]", propertyData.failureExpression.put()); + } + + propertyData.size = size; + propertyData.iteratorInstance = pIterator; + return S_OK; +} + +HRESULT object_visualizer::EnsurePseudoProperties() +{ + auto valueHome = make_com_ptr(m_pVisualizedExpression->ValueHome()); + auto pObject = DkmPointerValueHome::TryCast(valueHome.get()); + if (!pObject) + { + return E_NOINTERFACE; + } + + if (m_pseudoPropertyData.m_vectorPropertyData) + { + IF_FAIL_RET(EnsureVectorPseudoProperties(m_pVisualizedExpression.get(), pObject, m_objectType, *m_pseudoPropertyData.m_vectorPropertyData)); + } + return S_OK; +} + +size_t object_visualizer::GetPseudoPropertyCount() const +{ + size_t result = 0; + if (m_pseudoPropertyData.m_iteratorPropertyData) + { + ++result; + } + if (m_pseudoPropertyData.m_vectorPropertyData) + { + if (m_pseudoPropertyData.m_vectorPropertyData->size) + { + result += m_pseudoPropertyData.m_vectorPropertyData->size.value(); + } + else + { + // Save one space for the catch-all failure + ++result; + } + } + return result; +} + diff --git a/natvis/object_visualizer.h b/natvis/object_visualizer.h index ad83d79d7..7fc8074b7 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, @@ -37,6 +42,47 @@ struct PropertyData std::wstring displayName; }; +// Special case for IIterator`1 +struct IteratorPropertyData +{ + IteratorPropertyData() + { + currentProperty.index = -1; + hasCurrentProperty.index = -1; + } + std::optional hasCurrent; + PropertyData currentProperty; + PropertyData hasCurrentProperty; +}; + +// Special case for IVector`1 and IVectorView`1 +struct VectorPropertyData +{ + VectorPropertyData() + { + firstProperty.index = -1; + moveNextProperty.index = -1; + sizeProperty.index = -1; + } + std::optional size; + PropertyData sizeProperty; + PropertyData firstProperty; // IIterable`1.First() + PropertyData moveNextProperty; // IIterator`1.MoveNext() + PropertyData currentProperty; // IIterator`1.Current() + winrt::com_ptr iteratorInstance; + + // Each element will be evaludated on the fly, but if we fail to initially get Size or IIterable.First(), stash a catch-all failure here. + winrt::com_ptr failureExpression; + + std::vector> elementExpressions; +}; + +struct PseudoPropertyData +{ + std::unique_ptr m_iteratorPropertyData; + std::unique_ptr m_vectorPropertyData; +}; + // object_visualizer provides the visualization data model for WinRT objects, // both for root-level RAII IInspectables, and for nested ABI IInspectable properties. struct __declspec(uuid("c7da92da-3bc9-4312-8a93-46f480663980")) @@ -74,8 +120,18 @@ object_visualizer : winrt::implements private: void GetPropertyData(); void GetTypeProperties(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& type_name); + size_t GetChildCount() const { return m_propertyData.size() + GetPseudoPropertyCount(); } winrt::com_ptr m_pVisualizedExpression; ObjectType m_objectType; std::vector m_propertyData; bool m_isStringable{ false }; + PseudoPropertyData m_pseudoPropertyData; + + HRESULT EnsurePseudoProperties(); + HRESULT EvaluatePseudoProperties( + _In_ Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pVisualizedExpression, + size_t StartIndex, + size_t Count, + _Out_writes_(Count) Microsoft::VisualStudio::Debugger::Evaluation::DkmChildVisualizedExpression** expressions); + size_t GetPseudoPropertyCount() const; }; diff --git a/natvis/packages.config b/natvis/packages.config index 356e82f7f..7907cba44 100644 --- a/natvis/packages.config +++ b/natvis/packages.config @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/natvis/pch.h b/natvis/pch.h index 824d3aac1..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); } } @@ -91,8 +92,9 @@ 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); 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..de8af71f4 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); @@ -278,25 +278,38 @@ 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) { - if (auto found = _cache.find(index); found != _cache.end()) + coded_index index; + if (auto ptrIndex = std::get_if>(&typeSig.Type())) { - return found->second; - } + 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 {}; - } + 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 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; + 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 { ResolveType(process, index), guid }; + } + return {}; }; void ClearTypeResolver()