using System; using System.Collections.Generic; using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; namespace Semmle.Extraction.PDB { /// /// A sequencepoint is a marker in the source code where you can put a breakpoint, and /// maps instructions to source code. /// public struct SequencePoint { /// /// The byte-offset of the instruction. /// public int Offset { get; } /// /// The source location of the instruction. /// public Location Location { get; } public override string ToString() { return string.Format("{0} = {1}", Offset, Location); } public SequencePoint(int offset, Location location) { Offset = offset; Location = location; } } /// /// A location in source code. /// public sealed class Location { /// /// The file containing the code. /// public ISourceFile File { get; } /// /// The start line of text within the source file. /// public int StartLine { get; } /// /// The start column of text within the source file. /// public int StartColumn { get; } /// /// The end line of text within the source file. /// public int EndLine { get; } /// /// The end column of text within the source file. /// public int EndColumn { get; } public override string ToString() { return string.Format("({0},{1})-({2},{3})", StartLine, StartColumn, EndLine, EndColumn); } public override bool Equals(object? obj) { return obj is Location otherLocation && File.Equals(otherLocation.File) && StartLine == otherLocation.StartLine && StartColumn == otherLocation.StartColumn && EndLine == otherLocation.EndLine && EndColumn == otherLocation.EndColumn; } public override int GetHashCode() { var h1 = StartLine + 37 * (StartColumn + 51 * (EndLine + 97 * EndColumn)); return File.GetHashCode() + 17 * h1; } public Location(ISourceFile file, int startLine, int startCol, int endLine, int endCol) { File = file; StartLine = startLine; StartColumn = startCol; EndLine = endLine; EndColumn = endCol; } } public interface IMethod { IEnumerable SequencePoints { get; } Location Location { get; } } internal class Method : IMethod { public IEnumerable SequencePoints { get; } public Method(IEnumerable sequencePoints) { SequencePoints = sequencePoints; } public Location Location => SequencePoints.First().Location; } /// /// A source file reference in a PDB file. /// public interface ISourceFile { string Path { get; } /// /// The contents of the file. /// This property is needed in case the contents /// of the file are embedded in the PDB instead of being on the filesystem. /// /// null if the contents are unavailable. /// E.g. if the PDB file exists but the corresponding source files are missing. /// string? Contents { get; } } /// /// Wrapper for reading PDB files. /// This is needed because there are different libraries for dealing with /// different types of PDB file, even though they share the same file extension. /// public interface IPdb : IDisposable { /// /// Gets all source files in this PDB. /// IEnumerable SourceFiles { get; } /// /// Look up a method from a given handle. /// /// The handle to query. /// The method information, or null if the method does not have debug information. IMethod? GetMethod(MethodDebugInformationHandle methodHandle); } internal class PdbReader { /// /// Returns the PDB information associated with an assembly. /// /// The path to the assembly. /// The PE reader for the assembly. /// A PdbReader, or null if no PDB information is available. public static IPdb? Create(string assemblyPath, PEReader peReader) { return (IPdb?)MetadataPdbReader.CreateFromAssembly(assemblyPath, peReader) ?? NativePdbReader.CreateFromAssembly(peReader); } } }