Skip to content
Draft
49 changes: 49 additions & 0 deletions src/AsmResolver.DotNet/DefaultSymbolReaderFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.IO;
using System.IO.Compression;
using System.Linq;
using AsmResolver.DotNet.PortablePdbs;
using AsmResolver.DotNet.PortablePdbs.Serialized;
using AsmResolver.DotNet.Serialized;
using AsmResolver.PE.Debug;

namespace AsmResolver.DotNet;

public class DefaultSymbolReaderFactory : ISymbolReaderFactory
{
public static DefaultSymbolReaderFactory Instance { get; } = new();

public static SerializedPortablePdb? GetPdb(SerializedModuleDefinition module)
{
if (module.DotNetDirectory.Metadata is { } metadata && PortablePdb.TryFromMetadata(metadata, module, out var pdb))
{
return pdb;
}

// System.Reflection.Metadata tries reading from a file first, so we will as well
if (module.FilePath is { } path && PortablePdb.TryFromFile(Path.ChangeExtension(path, ".pdb"), module, out pdb))
{
return pdb;
}

var pdbSection = module.DebugData.Select(dd => dd.Contents).OfType<PortablePdbDataSegment>().FirstOrDefault();
if (pdbSection is not null)
{
var memoryStream = new MemoryStream(pdbSection.CompressedContents.ToArray());
var decompressStream = new DeflateStream(memoryStream, CompressionMode.Decompress);
var pdbData = new byte[pdbSection.UncompressedSize];
var resultStream = new MemoryStream(pdbData);
decompressStream.CopyTo(resultStream);
if (PortablePdb.TryFromBytes(pdbData, module, out pdb))
{
return pdb;
}
}

return null;
}

public ISymbolReader CreateSymbolReader(SerializedModuleDefinition module)
{
return GetPdb(module) is { } pdb ? new PortablePdbSymbolReader(pdb) : NullSymbolReader.Instance;
}
}
15 changes: 15 additions & 0 deletions src/AsmResolver.DotNet/ISymbolReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Collections.Generic;
using AsmResolver.DotNet.PortablePdbs;
using AsmResolver.DotNet.Serialized;

namespace AsmResolver.DotNet;

public interface ISymbolReader
{
IEnumerable<Document> GetDocuments();
MethodDebugInformation? GetMethodDebugInformation(SerializedMethodDefinition method);
IEnumerable<LocalScope> GetLocalScopes(SerializedMethodDefinition method);
MethodDefinition? GetKickoffMethod(SerializedMethodDefinition method);
MethodDefinition? GetMoveNextMethod(SerializedMethodDefinition method);
IEnumerable<CustomDebugInformation> GetCustomDebugInformations(IHasCustomDebugInformation member);
}
8 changes: 8 additions & 0 deletions src/AsmResolver.DotNet/ISymbolReaderFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using AsmResolver.DotNet.Serialized;

namespace AsmResolver.DotNet;

public interface ISymbolReaderFactory
{
ISymbolReader CreateSymbolReader(SerializedModuleDefinition module);
}
33 changes: 32 additions & 1 deletion src/AsmResolver.DotNet/MethodDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using AsmResolver.DotNet.Code.Cil;
using AsmResolver.DotNet.Code.Native;
using AsmResolver.DotNet.Collections;
using AsmResolver.DotNet.PortablePdbs;
using AsmResolver.DotNet.Signatures;
using AsmResolver.PE.DotNet.Cil;
using AsmResolver.PE.DotNet.Metadata.Tables;
Expand All @@ -25,7 +26,8 @@ public partial class MethodDefinition :
IHasGenericParameters,
IMemberForwarded,
IHasSecurityDeclaration,
IManagedEntryPoint
IManagedEntryPoint,
IHasCustomDebugInformation
{
private ParameterCollection? _parameters;

Expand Down Expand Up @@ -795,6 +797,25 @@ public partial UnmanagedExportInfo? ExportInfo
set;
}

[LazyProperty]
public partial MethodDebugInformation? MethodDebugInformation
{
get;
set;
}

[LazyProperty]
public partial IList<LocalScope> LocalScopes { get; }

[LazyProperty(OwnerProperty = nameof(MoveNextMethod))]
public partial MethodDefinition? KickoffMethod { get; set; }

[LazyProperty(OwnerProperty = nameof(KickoffMethod))]
public partial MethodDefinition? MoveNextMethod { get; set; }

[LazyProperty]
public partial IList<CustomDebugInformation> CustomDebugInformations { get; }

/// <summary>
/// Creates a new private static constructor for a type that is executed when its declaring type is loaded by the CLR.
/// </summary>
Expand Down Expand Up @@ -1031,6 +1052,16 @@ protected virtual IList<GenericParameter> GetGenericParameters() =>
/// </remarks>
protected virtual UnmanagedExportInfo? GetExportInfo() => null;

protected virtual MethodDebugInformation? GetMethodDebugInformation() => null;

protected virtual IList<LocalScope> GetLocalScopes() => new MemberCollection<MethodDefinition, LocalScope>(this);

protected virtual MethodDefinition? GetKickoffMethod() => null;

protected virtual MethodDefinition? GetMoveNextMethod() => null;

protected virtual IList<CustomDebugInformation> GetCustomDebugInformations() => new MemberCollection<IHasCustomDebugInformation, CustomDebugInformation>(this);

/// <summary>
/// Asserts whether the method's metadata is consistent with its signature.
/// </summary>
Expand Down
15 changes: 14 additions & 1 deletion src/AsmResolver.DotNet/ModuleDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using System.Threading;
using AsmResolver.Collections;
using AsmResolver.DotNet.Builder;
using AsmResolver.DotNet.Collections;
using AsmResolver.DotNet.PortablePdbs;
using AsmResolver.DotNet.Serialized;
using AsmResolver.DotNet.Signatures;
using AsmResolver.IO;
Expand All @@ -30,7 +32,8 @@ public partial class ModuleDefinition :
IResolutionScope,
IHasCustomAttribute,
IOwnedCollectionElement<AssemblyDefinition>,
ITypeOwner
ITypeOwner,
IHasCustomDebugInformation
{
private IList<TypeDefinition>? _topLevelTypes;
private IList<AssemblyReference>? _assemblyReferences;
Expand Down Expand Up @@ -797,6 +800,12 @@ public partial IManagedEntryPoint? ManagedEntryPoint
set;
}

[LazyProperty]
public partial IList<Document> Documents { get; }

[LazyProperty]
public partial IList<CustomDebugInformation> CustomDebugInformations { get; }

/// <summary>
/// Gets the default importer instance for this module.
/// </summary>
Expand Down Expand Up @@ -1237,6 +1246,10 @@ protected virtual IList<CustomAttribute> GetCustomAttributes() =>
/// </remarks>
protected virtual ReferenceImporter GetDefaultImporter() => new(this);

protected virtual IList<Document> GetDocuments() => new MemberCollection<ModuleDefinition, Document>(this);

protected virtual IList<CustomDebugInformation> GetCustomDebugInformations() => new MemberCollection<IHasCustomDebugInformation, CustomDebugInformation>(this);

/// <summary>
/// Detects the runtime that this module targets.
/// </summary>
Expand Down
22 changes: 22 additions & 0 deletions src/AsmResolver.DotNet/NullSymbolReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Collections.Generic;
using AsmResolver.DotNet.PortablePdbs;
using AsmResolver.DotNet.Serialized;

namespace AsmResolver.DotNet;

public class NullSymbolReader : ISymbolReader
{
public static NullSymbolReader Instance { get; } = new();

public IEnumerable<Document> GetDocuments() => [];

public MethodDebugInformation? GetMethodDebugInformation(SerializedMethodDefinition method) => null;

public IEnumerable<LocalScope> GetLocalScopes(SerializedMethodDefinition method) => [];

public MethodDefinition? GetKickoffMethod(SerializedMethodDefinition method) => null;

public MethodDefinition? GetMoveNextMethod(SerializedMethodDefinition method) => null;

public IEnumerable<CustomDebugInformation> GetCustomDebugInformations(IHasCustomDebugInformation member) => [];
}
36 changes: 36 additions & 0 deletions src/AsmResolver.DotNet/PortablePdbs/CustomDebugInformation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using AsmResolver.Collections;
using AsmResolver.DotNet.PortablePdbs.CustomRecords;
using AsmResolver.PE.DotNet.Metadata.Tables;

namespace AsmResolver.DotNet.PortablePdbs;

public partial class CustomDebugInformation : IMetadataMember, IOwnedCollectionElement<IHasCustomDebugInformation>
{
public CustomDebugInformation(MetadataToken token)
{
MetadataToken = token;
}

public MetadataToken MetadataToken
{
get;
}

[LazyProperty]
public partial IHasCustomDebugInformation? Owner
{
get;
set;
}

[LazyProperty]
public partial CustomDebugRecord? Value
{
get;
set;
}

protected virtual IHasCustomDebugInformation? GetOwner() => null;

protected virtual CustomDebugRecord? GetValue() => null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using AsmResolver.DotNet.PortablePdbs.Serialized;
using AsmResolver.DotNet.Signatures;
using AsmResolver.IO;
using AsmResolver.PE.DotNet.Metadata.Tables;

namespace AsmResolver.DotNet.PortablePdbs.CustomRecords;

public class AsyncMethodSteppingInformationRecord : CustomDebugRecord
{
public static Guid KnownKind { get; } = new("54FD2AC5-E925-401A-9C2A-F94F171072F8");

public override Guid Kind => KnownKind;

public override bool HasBlob => true;

public uint CatchHandlerOffset { get; set; }

public OffsetPair[]? Pairs { get; set; }

public static unsafe AsyncMethodSteppingInformationRecord FromReader(PdbReaderContext context, ref BinaryStreamReader reader)
{
var catchOffset = reader.ReadUInt32();
var pairList = new List<OffsetPair>();
while (reader.CanRead(1))
{
pairList.Add(new OffsetPair
{
YieldOffset = reader.ReadUInt32(),
ResumeOffset = reader.ReadUInt32(),
ResumeMethod = context.OwningModule.LookupMember<MethodDefinition>(new MetadataToken(TableIndex.Method, reader.ReadCompressedUInt32())),
});
}
return new AsyncMethodSteppingInformationRecord
{
CatchHandlerOffset = catchOffset,
Pairs = pairList.ToArray(),
};
}

protected override void WriteContents(in BlobSerializationContext context)
{
throw new NotImplementedException();
}

public struct OffsetPair
{
public uint YieldOffset { get; set; }
public uint ResumeOffset { get; set; }
public MethodDefinition ResumeMethod { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using AsmResolver.DotNet.PortablePdbs.Serialized;
using AsmResolver.DotNet.Signatures;
using AsmResolver.IO;
using AsmResolver.Shims;

namespace AsmResolver.DotNet.PortablePdbs.CustomRecords;

public class CompilationMetadataReferencesRecord : CustomDebugRecord
{
public static Guid KnownKind { get; } = new("7E4D4708-096E-4C5C-AEDA-CB10BA6A740D");

public override Guid Kind => KnownKind;

public override bool HasBlob => true;

public MetadataReferenceInfo[]? References { get; set; }

public static CompilationMetadataReferencesRecord FromReader(PdbReaderContext context, ref BinaryStreamReader reader)
{
var referenceList = new List<MetadataReferenceInfo>();
while (reader.CanRead(1))
{
var fileName = reader.ReadUtf8String();
var aliasesString = reader.ReadUtf8String();
Utf8String[] aliases;
if (aliasesString.GetBytesUnsafe().Contains((byte)','))
{
aliases = Array.ConvertAll(aliasesString.Value.Split(','), alias => (Utf8String)alias);
}
else if (aliasesString.Length != 0)
{
aliases = [aliasesString];
}
else
{
aliases = ArrayShim.Empty<Utf8String>();
}
var flags = (MetadataReferenceFlags)reader.ReadByte();
var timeStamp = new DateTime(1970, 1, 1) + TimeSpan.FromSeconds(reader.ReadUInt32());
var fileSize = reader.ReadUInt32();
var mvid = new Guid(reader.ReadBytes(16));

referenceList.Add(new MetadataReferenceInfo
{
FileName = fileName,
Aliases = aliases,
Flags = flags,
TimeStamp = timeStamp,
FileSize = fileSize,
Mvid = mvid,
});
}

return new CompilationMetadataReferencesRecord
{
References = referenceList.ToArray(),
};
}

protected override void WriteContents(in BlobSerializationContext context)
{
throw new NotImplementedException();
}

public struct MetadataReferenceInfo
{
public Utf8String? FileName { get; set; }
public Utf8String[]? Aliases { get; set; }
public MetadataReferenceFlags Flags { get; set; }
public DateTime TimeStamp { get; set; }
public uint FileSize { get; set; }
public Guid Mvid { get; set; }
}

public enum MetadataReferenceFlags
{
None = 0,
AssemblyReference = 1,
EmbeddedInteropTypes = 2,
}
}
Loading
Loading