Browse Source

Read and use tuple element names and dynamic type information from PDBs

pull/3114/head
ElektroKill 2 years ago
parent
commit
5b526cfeac
No known key found for this signature in database
GPG Key ID: 7E3C5C084E40E3EC
  1. 1
      ICSharpCode.Decompiler/DebugInfo/IDebugInfoProvider.cs
  2. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  3. 151
      ICSharpCode.Decompiler/IL/ApplyPdbLocalTypeInfoTypeVisitor.cs
  4. 10
      ICSharpCode.Decompiler/IL/ILReader.cs
  5. 12
      ICSharpCode.ILSpyX/PdbProvider/MonoCecilDebugInfoProvider.cs
  6. 73
      ICSharpCode.ILSpyX/PdbProvider/PortableDebugInfoProvider.cs

1
ICSharpCode.Decompiler/DebugInfo/IDebugInfoProvider.cs

@ -23,6 +23,7 @@ namespace ICSharpCode.Decompiler.DebugInfo @@ -23,6 +23,7 @@ namespace ICSharpCode.Decompiler.DebugInfo
IList<SequencePoint> GetSequencePoints(MethodDefinitionHandle method);
IList<Variable> GetVariables(MethodDefinitionHandle method);
bool TryGetName(MethodDefinitionHandle method, int index, out string name);
bool TryGetExtraTypeInfo(MethodDefinitionHandle method, int index, out string[] tupleElementNames, out bool[] dynamicFlags);
string SourceFileName { get; }
}
}

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -95,6 +95,7 @@ @@ -95,6 +95,7 @@
<Compile Include="DecompilationProgress.cs" />
<Compile Include="Disassembler\IEntityProcessor.cs" />
<Compile Include="Disassembler\SortByNameProcessor.cs" />
<Compile Include="IL\ApplyPdbLocalTypeInfoTypeVisitor.cs" />
<Compile Include="NRTAttributes.cs" />
<Compile Include="PartialTypeInfo.cs" />
<Compile Include="CSharp\ProjectDecompiler\IProjectFileWriter.cs" />

151
ICSharpCode.Decompiler/IL/ApplyPdbLocalTypeInfoTypeVisitor.cs

@ -0,0 +1,151 @@ @@ -0,0 +1,151 @@
using System;
using System.Collections.Immutable;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>
/// Heavily based on <see cref="ApplyAttributeTypeVisitor"/>
/// </summary>
sealed class ApplyPdbLocalTypeInfoTypeVisitor : TypeVisitor
{
private readonly bool[] dynamicData;
private readonly string[] tupleElementNames;
private int dynamicTypeIndex = 0;
private int tupleTypeIndex = 0;
public ApplyPdbLocalTypeInfoTypeVisitor(bool[] dynamicData, string[] tupleElementNames)
{
this.dynamicData = dynamicData;
this.tupleElementNames = tupleElementNames;
}
public override IType VisitModOpt(ModifiedType type)
{
dynamicTypeIndex++;
return base.VisitModOpt(type);
}
public override IType VisitModReq(ModifiedType type)
{
dynamicTypeIndex++;
return base.VisitModReq(type);
}
public override IType VisitPointerType(PointerType type)
{
dynamicTypeIndex++;
return base.VisitPointerType(type);
}
public override IType VisitArrayType(ArrayType type)
{
dynamicTypeIndex++;
return base.VisitArrayType(type);
}
public override IType VisitByReferenceType(ByReferenceType type)
{
dynamicTypeIndex++;
return base.VisitByReferenceType(type);
}
public override IType VisitTupleType(TupleType type)
{
if (tupleElementNames != null && tupleTypeIndex < tupleElementNames.Length)
{
int tupleCardinality = type.Cardinality;
string[] extractedValues = new string[tupleCardinality];
Array.Copy(tupleElementNames, tupleTypeIndex, extractedValues, 0,
Math.Min(tupleCardinality, tupleElementNames.Length - tupleTypeIndex));
var elementNames = ImmutableArray.CreateRange(extractedValues);
tupleTypeIndex += tupleCardinality;
int level = 0;
var elementTypes = new IType[type.ElementTypes.Length];
for (int i = 0; i < type.ElementTypes.Length; i++)
{
dynamicTypeIndex++;
IType elementType = type.ElementTypes[i];
if (i != 0 && (i - level) % TupleType.RestPosition == 0 && elementType is TupleType tuple)
{
tupleTypeIndex += tuple.Cardinality;
level++;
}
elementTypes[i] = elementType.AcceptVisitor(this);
}
return new TupleType(
type.Compilation,
elementTypes.ToImmutableArray(),
elementNames,
type.GetDefinition()?.ParentModule
);
}
return base.VisitTupleType(type);
}
public override IType VisitParameterizedType(ParameterizedType type)
{
if (TupleType.IsTupleCompatible(type, out var tupleCardinality))
tupleTypeIndex += tupleCardinality;
// Visit generic type and type arguments.
// Like base implementation, except that it increments dynamicTypeIndex.
var genericType = type.GenericType.AcceptVisitor(this);
bool changed = type.GenericType != genericType;
var arguments = new IType[type.TypeArguments.Count];
for (int i = 0; i < type.TypeArguments.Count; i++)
{
dynamicTypeIndex++;
arguments[i] = type.TypeArguments[i].AcceptVisitor(this);
changed = changed || arguments[i] != type.TypeArguments[i];
}
if (!changed)
return type;
return new ParameterizedType(genericType, arguments);
}
public override IType VisitFunctionPointerType(FunctionPointerType type)
{
dynamicTypeIndex++;
if (type.ReturnIsRefReadOnly)
{
dynamicTypeIndex++;
}
var returnType = type.ReturnType.AcceptVisitor(this);
bool changed = type.ReturnType != returnType;
var parameters = new IType[type.ParameterTypes.Length];
for (int i = 0; i < parameters.Length; i++)
{
dynamicTypeIndex += type.ParameterReferenceKinds[i] switch {
ReferenceKind.None => 1,
ReferenceKind.Ref => 1,
ReferenceKind.Out => 2, // in/out also count the modreq
ReferenceKind.In => 2,
_ => throw new NotSupportedException()
};
parameters[i] = type.ParameterTypes[i].AcceptVisitor(this);
changed = changed || parameters[i] != type.ParameterTypes[i];
}
if (!changed)
return type;
return type.WithSignature(returnType, parameters.ToImmutableArray());
}
public override IType VisitTypeDefinition(ITypeDefinition type)
{
IType newType = type;
var ktc = type.KnownTypeCode;
if (ktc == KnownTypeCode.Object && dynamicData is not null)
{
if (dynamicTypeIndex >= dynamicData.Length)
newType = SpecialType.Dynamic;
else if (dynamicData[dynamicTypeIndex])
newType = SpecialType.Dynamic;
}
return newType;
}
}
}

10
ICSharpCode.Decompiler/IL/ILReader.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// Copyright (c) 2014 Daniel Grunwald
// Copyright (c) 2014 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
@ -304,6 +304,14 @@ namespace ICSharpCode.Decompiler.IL @@ -304,6 +304,14 @@ namespace ICSharpCode.Decompiler.IL
{
kind = VariableKind.Local;
}
if (UseDebugSymbols && DebugInfo is not null &&
DebugInfo.TryGetExtraTypeInfo((MethodDefinitionHandle)method.MetadataToken, index,
out string[] tupleElementNames, out bool[] dynamicFlags))
{
type = type.AcceptVisitor(new ApplyPdbLocalTypeInfoTypeVisitor(dynamicFlags, tupleElementNames));
}
ILVariable ilVar = new ILVariable(kind, type, index);
if (!UseDebugSymbols || DebugInfo == null || !DebugInfo.TryGetName((MethodDefinitionHandle)method.MetadataToken, index, out string name))
{

12
ICSharpCode.ILSpyX/PdbProvider/MonoCecilDebugInfoProvider.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// Copyright (c) 2018 Siegfried Pammer
// Copyright (c) 2018 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
@ -135,5 +135,15 @@ namespace ICSharpCode.ILSpyX.PdbProvider @@ -135,5 +135,15 @@ namespace ICSharpCode.ILSpyX.PdbProvider
name = variable.Name;
return name != null;
}
public bool TryGetExtraTypeInfo(SRM.MethodDefinitionHandle method, int index,
[NotNullWhen(true)] out string[]? tupleElementNames, [NotNullWhen(true)] out bool[]? dynamicFlags)
{
// Mono.Cecil's WindowsPDB reader is unable to read tuple element names
// and dynamic flags custom debug information.
tupleElementNames = null;
dynamicFlags = null;
return false;
}
}
}

73
ICSharpCode.ILSpyX/PdbProvider/PortableDebugInfoProvider.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// Copyright (c) 2018 Siegfried Pammer
// Copyright (c) 2018 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
@ -161,5 +161,76 @@ namespace ICSharpCode.ILSpyX.PdbProvider @@ -161,5 +161,76 @@ namespace ICSharpCode.ILSpyX.PdbProvider
}
return false;
}
public bool TryGetExtraTypeInfo(MethodDefinitionHandle method, int index,
[NotNullWhen(true)] out string?[]? tupleElementNames, [NotNullWhen(true)] out bool[]? dynamicFlags)
{
var metadata = GetMetadataReader();
tupleElementNames = null;
dynamicFlags = null;
if (metadata == null)
return false;
LocalVariableHandle localVariableHandle = default;
foreach (var h in metadata.GetLocalScopes(method))
{
var scope = metadata.GetLocalScope(h);
foreach (var v in scope.GetLocalVariables())
{
var var = metadata.GetLocalVariable(v);
if (var.Index == index)
{
localVariableHandle = v;
break;
}
}
if (!localVariableHandle.IsNil)
break;
}
foreach (var h in metadata.CustomDebugInformation)
{
var cdi = metadata.GetCustomDebugInformation(h);
if (cdi.Parent.IsNil || cdi.Parent.Kind != HandleKind.LocalVariable)
continue;
if (localVariableHandle != (LocalVariableHandle)cdi.Parent)
continue;
if (cdi.Value.IsNil || cdi.Kind.IsNil)
continue;
var reader = metadata.GetBlobReader(cdi.Value);
var kind = metadata.GetGuid(cdi.Kind);
if (kind == KnownGuids.TupleElementNames && tupleElementNames is null)
{
var list = new List<string?>();
while (reader.RemainingBytes > 0)
{
int length = reader.IndexOf(0);
string s = reader.ReadUTF8(length);
reader.ReadByte();
list.Add(string.IsNullOrWhiteSpace(s) ? null : s);
}
tupleElementNames = list.ToArray();
}
else if (kind == KnownGuids.DynamicLocalVariables && dynamicFlags is null)
{
dynamicFlags = new bool[reader.Length * 8];
int j = 0;
while (reader.Offset < reader.Length)
{
int b = reader.ReadByte();
for (int i = 1; i < 0x100; i <<= 1)
dynamicFlags[j++] = (b & i) != 0;
}
}
if (tupleElementNames != null && dynamicFlags != null)
break;
}
return tupleElementNames != null || dynamicFlags != null;
}
}
}

Loading…
Cancel
Save