Skip to content

Commit 96afc17

Browse files
authored
Add support for generic interfaces on non-generic types (runtime classes). Next up: add support for types returning generic interfaces from GetRuntimeClassName (e.g., C++/WinRT collection helpers) (#497)
1 parent 97a7b4e commit 96afc17

File tree

6 files changed

+449
-131
lines changed

6 files changed

+449
-131
lines changed

natvis/cppwinrt_visualizer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ cppwinrt_visualizer::cppwinrt_visualizer()
167167

168168
cppwinrt_visualizer::~cppwinrt_visualizer()
169169
{
170+
ClearTypeResolver();
170171
db_files.clear();
171172
db.reset();
172173
}

natvis/cppwinrtvisualizer.vcxproj

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
<Optimization>Disabled</Optimization>
103103
<SDLCheck>false</SDLCheck>
104104
<PreprocessorDefinitions>WIN32;_DEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
105-
<AdditionalIncludeDirectories>$(IntDir);..\cppwinrt;$(DIASDKInc);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
105+
<AdditionalIncludeDirectories>$(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
106106
<LanguageStandard>stdcpp17</LanguageStandard>
107107
<AdditionalOptions>/await</AdditionalOptions>
108108
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
@@ -128,7 +128,7 @@
128128
<Optimization>Disabled</Optimization>
129129
<SDLCheck>false</SDLCheck>
130130
<PreprocessorDefinitions>_DEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
131-
<AdditionalIncludeDirectories>$(IntDir);..\cppwinrt;$(DIASDKInc);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
131+
<AdditionalIncludeDirectories>$(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
132132
<LanguageStandard>stdcpp17</LanguageStandard>
133133
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
134134
<AdditionalOptions>/await</AdditionalOptions>
@@ -155,7 +155,7 @@
155155
<IntrinsicFunctions>true</IntrinsicFunctions>
156156
<SDLCheck>false</SDLCheck>
157157
<PreprocessorDefinitions>WIN32;NDEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
158-
<AdditionalIncludeDirectories>$(IntDir);..\cppwinrt;$(DIASDKInc);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
158+
<AdditionalIncludeDirectories>$(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
159159
<LanguageStandard>stdcpp17</LanguageStandard>
160160
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
161161
<AdditionalOptions>/await</AdditionalOptions>
@@ -184,7 +184,7 @@
184184
<IntrinsicFunctions>true</IntrinsicFunctions>
185185
<SDLCheck>false</SDLCheck>
186186
<PreprocessorDefinitions>NDEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
187-
<AdditionalIncludeDirectories>$(IntDir);..\cppwinrt;$(DIASDKInc);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
187+
<AdditionalIncludeDirectories>$(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
188188
<LanguageStandard>stdcpp17</LanguageStandard>
189189
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
190190
<AdditionalOptions>/await</AdditionalOptions>
@@ -223,6 +223,7 @@
223223
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
224224
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
225225
</ClCompile>
226+
<ClCompile Include="type_resolver.cpp" />
226227
</ItemGroup>
227228
<ItemGroup>
228229
<VsdConfigXmlFiles Condition="'$(VisualStudioVersion)' == '15.0'" Include="cppwinrtvisualizer15.vsdconfigxml" />

natvis/object_visualizer.cpp

Lines changed: 78 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,8 @@ using namespace Microsoft::VisualStudio::Debugger;
77
using namespace Microsoft::VisualStudio::Debugger::Evaluation;
88
using namespace std::literals;
99
using namespace winrt;
10-
using namespace winmd::impl;
1110
using namespace winmd::reader;
1211

13-
template <typename...T> struct overloaded : T... { using T::operator()...; };
14-
template <typename...T> overloaded(T...)->overloaded<T...>;
15-
1612
#define IID_IInspectable L"AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90"
1713
#define IID_IStringable L"96369F54-8EB6-48F0-ABCE-C1B211E627C3"
1814

@@ -351,56 +347,11 @@ struct property_type
351347
};
352348

353349
void GetInterfaceData(
354-
DkmProcess* process,
355350
coded_index<TypeDefOrRef> index,
356351
_Inout_ std::vector<PropertyData>& propertyData,
357352
_Out_ bool& isStringable
358353
){
359-
auto resolve_type_index = [](coded_index<TypeDefOrRef> index) -> TypeDef
360-
{
361-
switch (index.type())
362-
{
363-
case TypeDefOrRef::TypeDef:
364-
{
365-
return index.TypeDef();
366-
}
367-
case TypeDefOrRef::TypeRef:
368-
{
369-
return find_required(index.TypeRef());
370-
}
371-
case TypeDefOrRef::TypeSpec:
372-
{
373-
auto type_signature = index.TypeSpec().Signature();
374-
auto signature = type_signature.GenericTypeInst();
375-
return find_required(signature.GenericType().TypeRef());
376-
}
377-
}
378-
return {};
379-
};
380-
381-
auto type = resolve_type_index(index);
382-
auto attribute = get_attribute(type, "Windows.Foundation.Metadata", "GuidAttribute");
383-
if (!attribute)
384-
{
385-
throw_invalid("'Windows.Foundation.Metadata.GuidAttribute' attribute for type '", type.TypeNamespace(), ".", type.TypeName(), "' not found");
386-
}
387-
auto args = attribute.Value().FixedArgs();
388-
std::string guid(68, '?');
389-
int count = sprintf_s(guid.data(), guid.size() + 1,
390-
"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"
391-
, std::get<uint32_t>(std::get<ElemSig>(args[0].value).value)
392-
, std::get<uint16_t>(std::get<ElemSig>(args[1].value).value)
393-
, std::get<uint16_t>(std::get<ElemSig>(args[2].value).value)
394-
, std::get<uint8_t>(std::get<ElemSig>(args[3].value).value)
395-
, std::get<uint8_t>(std::get<ElemSig>(args[4].value).value)
396-
, std::get<uint8_t>(std::get<ElemSig>(args[5].value).value)
397-
, std::get<uint8_t>(std::get<ElemSig>(args[6].value).value)
398-
, std::get<uint8_t>(std::get<ElemSig>(args[7].value).value)
399-
, std::get<uint8_t>(std::get<ElemSig>(args[8].value).value)
400-
, std::get<uint8_t>(std::get<ElemSig>(args[9].value).value)
401-
, std::get<uint8_t>(std::get<ElemSig>(args[10].value).value));
402-
guid.resize(count);
403-
std::wstring propIid(guid.cbegin(), guid.cend());
354+
auto [type, propIid] = ResolveTypeInterface(index);
404355

405356
if (propIid == IID_IStringable)
406357
{
@@ -424,89 +375,85 @@ void GetInterfaceData(
424375
std::wstring propDisplayType;
425376

426377
auto retType = method.Signature().ReturnType();
427-
std::visit(overloaded
378+
std::visit(overloaded{
379+
[&](ElementType type)
428380
{
429-
[&](ElementType type)
381+
if ((type < ElementType::Boolean) || (type > ElementType::String))
430382
{
431-
if ((type < ElementType::Boolean) || (type > ElementType::String))
432-
{
433-
return;
434-
}
435-
propCategory = (PropertyCategory)(static_cast<std::underlying_type<ElementType>::type>(type) -
436-
static_cast<std::underlying_type<ElementType>::type>(ElementType::Boolean));
437-
},
438-
[&](coded_index<TypeDefOrRef> const& index)
383+
return;
384+
}
385+
propCategory = (PropertyCategory)(static_cast<std::underlying_type<ElementType>::type>(type) -
386+
static_cast<std::underlying_type<ElementType>::type>(ElementType::Boolean));
387+
},
388+
[&](coded_index<TypeDefOrRef> const& index)
389+
{
390+
auto type = ResolveType(index);
391+
auto typeName = type.TypeName();
392+
if (typeName == "GUID"sv)
439393
{
440-
auto type = resolve_type_index(index);
441-
auto typeName = type.TypeName();
442-
if (typeName == "GUID"sv)
394+
propCategory = PropertyCategory::Guid;
395+
}
396+
else
397+
{
398+
auto ns = std::string(type.TypeNamespace());
399+
auto name = std::string(type.TypeName());
400+
401+
// Map numeric type names
402+
if (ns == "Windows.Foundation.Numerics")
443403
{
444-
propCategory = PropertyCategory::Guid;
404+
if (name == "Matrix3x2") { name = "float3x2"; }
405+
else if (name == "Matrix4x4") { name = "float4x4"; }
406+
else if (name == "Plane") { name = "plane"; }
407+
else if (name == "Quaternion") { name = "quaternion"; }
408+
else if (name == "Vector2") { name = "float2"; }
409+
else if (name == "Vector3") { name = "float3"; }
410+
else if (name == "Vector4") { name = "float4"; }
445411
}
446-
else
447-
{
448-
auto ns = std::string(type.TypeNamespace());
449-
auto name = std::string(type.TypeName());
450-
451-
// Map numeric type names
452-
if (ns == "Windows.Foundation.Numerics")
453-
{
454-
if (name == "Matrix3x2") { name = "float3x2"; }
455-
else if (name == "Matrix4x4") { name = "float4x4"; }
456-
else if (name == "Plane") { name = "plane"; }
457-
else if (name == "Quaternion") { name = "quaternion"; }
458-
else if (name == "Vector2") { name = "float2"; }
459-
else if (name == "Vector3") { name = "float3"; }
460-
else if (name == "Vector4") { name = "float4"; }
461-
}
462412

463-
// Types come back from winmd files with '.', need to be '::'
464-
// Ex. Windows.Foundation.Uri needs to be Windows::Foundation::Uri
465-
auto fullTypeName = ns + "::" + name;
466-
wchar_t cppTypename[500];
467-
size_t i, j;
468-
for (i = 0, j = 0; i < (fullTypeName.length() + 1); i++, j++)
469-
{
470-
if (fullTypeName[i] == L'.')
471-
{
472-
cppTypename[j++] = L':';
473-
cppTypename[j] = L':';
474-
}
475-
else
476-
{
477-
cppTypename[j] = fullTypeName[i];
478-
}
479-
}
480-
481-
propDisplayType = std::wstring(L"winrt::") + cppTypename;
482-
if(get_category(type) == category::class_type)
413+
// Types come back from winmd files with '.', need to be '::'
414+
// Ex. Windows.Foundation.Uri needs to be Windows::Foundation::Uri
415+
auto fullTypeName = ns + "::" + name;
416+
wchar_t cppTypename[500];
417+
size_t i, j;
418+
for (i = 0, j = 0; i < (fullTypeName.length() + 1); i++, j++)
419+
{
420+
if (fullTypeName[i] == L'.')
483421
{
484-
propCategory = PropertyCategory::Class;
485-
propAbiType = L"winrt::impl::inspectable_abi*";
422+
cppTypename[j++] = L':';
423+
cppTypename[j] = L':';
486424
}
487425
else
488426
{
489-
propCategory = PropertyCategory::Value;
490-
propAbiType = propDisplayType;
427+
cppTypename[j] = fullTypeName[i];
491428
}
492429
}
493-
},
494-
[&](GenericTypeIndex /*var*/)
495-
{
496-
// TODO: generic properties
497-
NatvisDiagnostic(process, L"Generics are not yet supported", NatvisDiagnosticLevel::Verbose);
498-
},
499-
[&](GenericMethodTypeIndex /*var*/)
500-
{
501-
throw_invalid("Generic methods not supported.");
502-
},
503-
[&](GenericTypeInstSig const& /*type*/)
504-
{
505-
// TODO: generic properties
506-
NatvisDiagnostic(process, L"Generics are not yet supported", NatvisDiagnosticLevel::Verbose);
430+
431+
propDisplayType = std::wstring(L"winrt::") + cppTypename;
432+
if(get_category(type) == category::class_type)
433+
{
434+
propCategory = PropertyCategory::Class;
435+
propAbiType = L"winrt::impl::inspectable_abi*";
436+
}
437+
else
438+
{
439+
propCategory = PropertyCategory::Value;
440+
propAbiType = propDisplayType;
441+
}
507442
}
508443
},
509-
retType.Type().Type());
444+
[&](GenericTypeIndex /*var*/)
445+
{
446+
throw_invalid("Generics are not yet supported");
447+
},
448+
[&](GenericMethodTypeIndex /*var*/)
449+
{
450+
throw_invalid("Generic methods not supported.");
451+
},
452+
[&](GenericTypeInstSig const& /*type*/)
453+
{
454+
throw_invalid("Generics are not yet supported");
455+
}
456+
}, retType.Type().Type());
510457

511458
auto propName = method.Name().substr(4);
512459
std::wstring propDisplayName(propName.cbegin(), propName.cend());
@@ -530,6 +477,7 @@ void object_visualizer::GetPropertyData()
530477

531478
void object_visualizer::GetTypeProperties(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& type_name)
532479
{
480+
// TODO: add support for direct generic interface implementations (e.g., key_value_pair)
533481
auto type = FindType(process, type_name);
534482
if (!type)
535483
{
@@ -550,17 +498,17 @@ void object_visualizer::GetTypeProperties(Microsoft::VisualStudio::Debugger::Dkm
550498
auto impls = type.InterfaceImpl();
551499
for (auto&& impl : impls)
552500
{
553-
GetInterfaceData(process, impl.Interface(), m_propertyData, m_isStringable);
501+
GetInterfaceData(impl.Interface(), m_propertyData, m_isStringable);
554502
}
555503
}
556504
else if (get_category(type) == category::interface_type)
557505
{
558506
auto impls = type.InterfaceImpl();
559507
for (auto&& impl : impls)
560508
{
561-
GetInterfaceData(process, impl.Interface(), m_propertyData, m_isStringable);
509+
GetInterfaceData(impl.Interface(), m_propertyData, m_isStringable);
562510
}
563-
GetInterfaceData(process, type.coded_index<TypeDefOrRef>(), m_propertyData, m_isStringable);
511+
GetInterfaceData(type.coded_index<TypeDefOrRef>(), m_propertyData, m_isStringable);
564512
}
565513
}
566514

@@ -630,6 +578,14 @@ HRESULT object_visualizer::GetChildren(
630578
{
631579
GetPropertyData();
632580
}
581+
catch (std::invalid_argument const& e)
582+
{
583+
std::string_view message(e.what());
584+
NatvisDiagnostic(m_pVisualizedExpression.get(),
585+
std::wstring(L"Exception in object_visualizer::GetPropertyData: ") +
586+
std::wstring(message.begin(), message.end()),
587+
NatvisDiagnosticLevel::Error, to_hresult());
588+
}
633589
catch (...)
634590
{
635591
NatvisDiagnostic(m_pVisualizedExpression.get(),

0 commit comments

Comments
 (0)