Skip to content

Commit 760e7e0

Browse files
Merge pull request #3114 from ElektroKill/pdb-extra-local-type-info
Read and use tuple element names and dynamic type information from PDBs
2 parents d2bf239 + eefb466 commit 760e7e0

File tree

6 files changed

+257
-3
lines changed

6 files changed

+257
-3
lines changed

ICSharpCode.Decompiler/DebugInfo/IDebugInfoProvider.cs

+7
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,19 @@ public Variable(int index, string name)
1717
public string Name { get; }
1818
}
1919

20+
public struct PdbExtraTypeInfo
21+
{
22+
public string[] TupleElementNames;
23+
public bool[] DynamicFlags;
24+
}
25+
2026
public interface IDebugInfoProvider
2127
{
2228
string Description { get; }
2329
IList<SequencePoint> GetSequencePoints(MethodDefinitionHandle method);
2430
IList<Variable> GetVariables(MethodDefinitionHandle method);
2531
bool TryGetName(MethodDefinitionHandle method, int index, out string name);
32+
bool TryGetExtraTypeInfo(MethodDefinitionHandle method, int index, out PdbExtraTypeInfo extraTypeInfo);
2633
string SourceFileName { get; }
2734
}
2835
}

ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
<Compile Include="DecompilationProgress.cs" />
9898
<Compile Include="Disassembler\IEntityProcessor.cs" />
9999
<Compile Include="Disassembler\SortByNameProcessor.cs" />
100+
<Compile Include="IL\ApplyPdbLocalTypeInfoTypeVisitor.cs" />
100101
<Compile Include="NRTAttributes.cs" />
101102
<Compile Include="PartialTypeInfo.cs" />
102103
<Compile Include="CSharp\ProjectDecompiler\IProjectFileWriter.cs" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
using System;
2+
using System.Collections.Immutable;
3+
4+
using ICSharpCode.Decompiler.DebugInfo;
5+
using ICSharpCode.Decompiler.TypeSystem;
6+
using ICSharpCode.Decompiler.TypeSystem.Implementation;
7+
8+
namespace ICSharpCode.Decompiler.IL
9+
{
10+
/// <summary>
11+
/// Heavily based on <see cref="ApplyAttributeTypeVisitor"/>
12+
/// </summary>
13+
sealed class ApplyPdbLocalTypeInfoTypeVisitor : TypeVisitor
14+
{
15+
private readonly bool[] dynamicData;
16+
private readonly string[] tupleElementNames;
17+
private int dynamicTypeIndex = 0;
18+
private int tupleTypeIndex = 0;
19+
20+
private ApplyPdbLocalTypeInfoTypeVisitor(bool[] dynamicData, string[] tupleElementNames)
21+
{
22+
this.dynamicData = dynamicData;
23+
this.tupleElementNames = tupleElementNames;
24+
}
25+
26+
public static IType Apply(IType type, PdbExtraTypeInfo pdbExtraTypeInfo)
27+
{
28+
if (pdbExtraTypeInfo.DynamicFlags is null && pdbExtraTypeInfo.TupleElementNames is null)
29+
return type;
30+
return type.AcceptVisitor(new ApplyPdbLocalTypeInfoTypeVisitor(pdbExtraTypeInfo.DynamicFlags, pdbExtraTypeInfo.TupleElementNames));
31+
}
32+
33+
public override IType VisitModOpt(ModifiedType type)
34+
{
35+
dynamicTypeIndex++;
36+
return base.VisitModOpt(type);
37+
}
38+
39+
public override IType VisitModReq(ModifiedType type)
40+
{
41+
dynamicTypeIndex++;
42+
return base.VisitModReq(type);
43+
}
44+
45+
public override IType VisitPointerType(PointerType type)
46+
{
47+
dynamicTypeIndex++;
48+
return base.VisitPointerType(type);
49+
}
50+
51+
public override IType VisitArrayType(ArrayType type)
52+
{
53+
dynamicTypeIndex++;
54+
return base.VisitArrayType(type);
55+
}
56+
57+
public override IType VisitByReferenceType(ByReferenceType type)
58+
{
59+
dynamicTypeIndex++;
60+
return base.VisitByReferenceType(type);
61+
}
62+
63+
public override IType VisitTupleType(TupleType type)
64+
{
65+
if (tupleElementNames != null && tupleTypeIndex < tupleElementNames.Length)
66+
{
67+
int tupleCardinality = type.Cardinality;
68+
string[] extractedValues = new string[tupleCardinality];
69+
Array.Copy(tupleElementNames, tupleTypeIndex, extractedValues, 0,
70+
Math.Min(tupleCardinality, tupleElementNames.Length - tupleTypeIndex));
71+
var elementNames = ImmutableArray.CreateRange(extractedValues);
72+
tupleTypeIndex += tupleCardinality;
73+
74+
int level = 0;
75+
var elementTypes = new IType[type.ElementTypes.Length];
76+
for (int i = 0; i < type.ElementTypes.Length; i++)
77+
{
78+
dynamicTypeIndex++;
79+
IType elementType = type.ElementTypes[i];
80+
if (i != 0 && (i - level) % TupleType.RestPosition == 0 && elementType is TupleType tuple)
81+
{
82+
tupleTypeIndex += tuple.Cardinality;
83+
level++;
84+
}
85+
elementTypes[i] = elementType.AcceptVisitor(this);
86+
}
87+
88+
return new TupleType(
89+
type.Compilation,
90+
elementTypes.ToImmutableArray(),
91+
elementNames,
92+
type.GetDefinition()?.ParentModule
93+
);
94+
}
95+
return base.VisitTupleType(type);
96+
}
97+
98+
public override IType VisitParameterizedType(ParameterizedType type)
99+
{
100+
if (TupleType.IsTupleCompatible(type, out var tupleCardinality))
101+
tupleTypeIndex += tupleCardinality;
102+
// Visit generic type and type arguments.
103+
// Like base implementation, except that it increments dynamicTypeIndex.
104+
var genericType = type.GenericType.AcceptVisitor(this);
105+
bool changed = type.GenericType != genericType;
106+
var arguments = new IType[type.TypeArguments.Count];
107+
for (int i = 0; i < type.TypeArguments.Count; i++)
108+
{
109+
dynamicTypeIndex++;
110+
arguments[i] = type.TypeArguments[i].AcceptVisitor(this);
111+
changed = changed || arguments[i] != type.TypeArguments[i];
112+
}
113+
if (!changed)
114+
return type;
115+
return new ParameterizedType(genericType, arguments);
116+
}
117+
118+
public override IType VisitFunctionPointerType(FunctionPointerType type)
119+
{
120+
dynamicTypeIndex++;
121+
if (type.ReturnIsRefReadOnly)
122+
{
123+
dynamicTypeIndex++;
124+
}
125+
var returnType = type.ReturnType.AcceptVisitor(this);
126+
bool changed = type.ReturnType != returnType;
127+
var parameters = new IType[type.ParameterTypes.Length];
128+
for (int i = 0; i < parameters.Length; i++)
129+
{
130+
dynamicTypeIndex += type.ParameterReferenceKinds[i] switch {
131+
ReferenceKind.None => 1,
132+
ReferenceKind.Ref => 1,
133+
ReferenceKind.Out => 2, // in/out also count the modreq
134+
ReferenceKind.In => 2,
135+
_ => throw new NotSupportedException()
136+
};
137+
parameters[i] = type.ParameterTypes[i].AcceptVisitor(this);
138+
changed = changed || parameters[i] != type.ParameterTypes[i];
139+
}
140+
if (!changed)
141+
return type;
142+
return type.WithSignature(returnType, parameters.ToImmutableArray());
143+
}
144+
145+
public override IType VisitTypeDefinition(ITypeDefinition type)
146+
{
147+
IType newType = type;
148+
var ktc = type.KnownTypeCode;
149+
if (ktc == KnownTypeCode.Object && dynamicData is not null)
150+
{
151+
if (dynamicTypeIndex >= dynamicData.Length)
152+
newType = SpecialType.Dynamic;
153+
else if (dynamicData[dynamicTypeIndex])
154+
newType = SpecialType.Dynamic;
155+
}
156+
return newType;
157+
}
158+
}
159+
}

ICSharpCode.Decompiler/IL/ILReader.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2014 Daniel Grunwald
1+
// Copyright (c) 2014 Daniel Grunwald
22
//
33
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
44
// software and associated documentation files (the "Software"), to deal in the Software
@@ -304,6 +304,13 @@ ILVariable CreateILVariable(int index, IType type)
304304
{
305305
kind = VariableKind.Local;
306306
}
307+
308+
if (UseDebugSymbols && DebugInfo is not null &&
309+
DebugInfo.TryGetExtraTypeInfo((MethodDefinitionHandle)method.MetadataToken, index, out var pdbExtraTypeInfo))
310+
{
311+
type = ApplyPdbLocalTypeInfoTypeVisitor.Apply(type, pdbExtraTypeInfo);
312+
}
313+
307314
ILVariable ilVar = new ILVariable(kind, type, index);
308315
if (!UseDebugSymbols || DebugInfo == null || !DebugInfo.TryGetName((MethodDefinitionHandle)method.MetadataToken, index, out string name))
309316
{

ICSharpCode.ILSpyX/PdbProvider/MonoCecilDebugInfoProvider.cs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2018 Siegfried Pammer
1+
// Copyright (c) 2018 Siegfried Pammer
22
//
33
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
44
// software and associated documentation files (the "Software"), to deal in the Software
@@ -135,5 +135,13 @@ public bool TryGetName(SRM.MethodDefinitionHandle handle, int index, [NotNullWhe
135135
name = variable.Name;
136136
return name != null;
137137
}
138+
139+
public bool TryGetExtraTypeInfo(SRM.MethodDefinitionHandle method, int index, out PdbExtraTypeInfo extraTypeInfo)
140+
{
141+
// Mono.Cecil's WindowsPDB reader is unable to read tuple element names
142+
// and dynamic flags custom debug information.
143+
extraTypeInfo = default;
144+
return false;
145+
}
138146
}
139147
}

ICSharpCode.ILSpyX/PdbProvider/PortableDebugInfoProvider.cs

+73-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2018 Siegfried Pammer
1+
// Copyright (c) 2018 Siegfried Pammer
22
//
33
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
44
// software and associated documentation files (the "Software"), to deal in the Software
@@ -161,5 +161,77 @@ public bool TryGetName(MethodDefinitionHandle method, int index, [NotNullWhen(tr
161161
}
162162
return false;
163163
}
164+
165+
public bool TryGetExtraTypeInfo(MethodDefinitionHandle method, int index, out PdbExtraTypeInfo extraTypeInfo)
166+
{
167+
var metadata = GetMetadataReader();
168+
extraTypeInfo = default;
169+
170+
if (metadata == null)
171+
return false;
172+
173+
LocalVariableHandle localVariableHandle = default;
174+
foreach (var h in metadata.GetLocalScopes(method))
175+
{
176+
var scope = metadata.GetLocalScope(h);
177+
foreach (var v in scope.GetLocalVariables())
178+
{
179+
var var = metadata.GetLocalVariable(v);
180+
if (var.Index == index)
181+
{
182+
localVariableHandle = v;
183+
break;
184+
}
185+
}
186+
187+
if (!localVariableHandle.IsNil)
188+
break;
189+
}
190+
191+
foreach (var h in metadata.CustomDebugInformation)
192+
{
193+
var cdi = metadata.GetCustomDebugInformation(h);
194+
if (cdi.Parent.IsNil || cdi.Parent.Kind != HandleKind.LocalVariable)
195+
continue;
196+
if (localVariableHandle != (LocalVariableHandle)cdi.Parent)
197+
continue;
198+
if (cdi.Value.IsNil || cdi.Kind.IsNil)
199+
continue;
200+
var kind = metadata.GetGuid(cdi.Kind);
201+
if (kind == KnownGuids.TupleElementNames && extraTypeInfo.TupleElementNames is null)
202+
{
203+
var reader = metadata.GetBlobReader(cdi.Value);
204+
var list = new List<string?>();
205+
while (reader.RemainingBytes > 0)
206+
{
207+
// Read a UTF8 null-terminated string
208+
int length = reader.IndexOf(0);
209+
string s = reader.ReadUTF8(length);
210+
// Skip null terminator
211+
reader.ReadByte();
212+
list.Add(string.IsNullOrWhiteSpace(s) ? null : s);
213+
}
214+
215+
extraTypeInfo.TupleElementNames = list.ToArray();
216+
}
217+
else if (kind == KnownGuids.DynamicLocalVariables && extraTypeInfo.DynamicFlags is null)
218+
{
219+
var reader = metadata.GetBlobReader(cdi.Value);
220+
extraTypeInfo.DynamicFlags = new bool[reader.Length * 8];
221+
int j = 0;
222+
while (reader.RemainingBytes > 0)
223+
{
224+
int b = reader.ReadByte();
225+
for (int i = 1; i < 0x100; i <<= 1)
226+
extraTypeInfo.DynamicFlags[j++] = (b & i) != 0;
227+
}
228+
}
229+
230+
if (extraTypeInfo.TupleElementNames != null && extraTypeInfo.DynamicFlags != null)
231+
break;
232+
}
233+
234+
return extraTypeInfo.TupleElementNames != null || extraTypeInfo.DynamicFlags != null;
235+
}
164236
}
165237
}

0 commit comments

Comments
 (0)