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);
}
}
}