using System; using System.IO; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; namespace Semmle.Extraction.CIL { /// /// Extraction context for CIL extraction. /// Adds additional context that is specific for CIL extraction. /// One context = one DLL/EXE. /// internal sealed partial class Context : Extraction.Context, IDisposable { private readonly FileStream stream; private Entities.Assembly? assemblyNull; public MetadataReader MdReader { get; } public PEReader PeReader { get; } public string AssemblyPath { get; } public Entities.Assembly Assembly { get { return assemblyNull!; } set { assemblyNull = value; } } public PDB.IPdb? Pdb { get; } public Context(Extractor extractor, TrapWriter trapWriter, string assemblyPath, bool extractPdbs) : base(extractor, trapWriter) { this.AssemblyPath = assemblyPath; stream = File.OpenRead(assemblyPath); PeReader = new PEReader(stream, PEStreamOptions.PrefetchEntireImage); MdReader = PeReader.GetMetadataReader(); TypeSignatureDecoder = new Entities.TypeSignatureDecoder(this); globalNamespace = new Lazy(() => Populate(new Entities.Namespace(this, "", null))); systemNamespace = new Lazy(() => Populate(new Entities.Namespace(this, "System"))); genericHandleFactory = new CachedFunction(CreateGenericHandle); namespaceFactory = new CachedFunction(n => CreateNamespace(MdReader.GetString(n))); namespaceDefinitionFactory = new CachedFunction(CreateNamespace); sourceFiles = new CachedFunction(path => new Entities.PdbSourceFile(this, path)); folders = new CachedFunction(path => new Entities.Folder(this, path)); sourceLocations = new CachedFunction(location => new Entities.PdbSourceLocation(this, location)); defaultGenericContext = new EmptyContext(this); if (extractPdbs) { Pdb = PDB.PdbReader.Create(assemblyPath, PeReader); if (Pdb is not null) { Extractor.Logger.Log(Util.Logging.Severity.Info, string.Format("Found PDB information for {0}", assemblyPath)); } } } public void Dispose() { if (Pdb is not null) Pdb.Dispose(); PeReader.Dispose(); stream.Dispose(); } /// /// Extract the contents of a given entity. /// /// The entity to extract. public void Extract(IExtractedEntity entity) { foreach (var content in entity.Contents) { content.Extract(this); } } public void WriteAssemblyPrefix(TextWriter trapFile) { var def = MdReader.GetAssemblyDefinition(); trapFile.Write(GetString(def.Name)); trapFile.Write('_'); trapFile.Write(def.Version.ToString()); trapFile.Write(Entities.Type.AssemblyTypeNameSeparator); } public Entities.TypeSignatureDecoder TypeSignatureDecoder { get; } /// /// A type used to signify something we can't handle yet. /// Specifically, function pointers (used in C++). /// public Entities.Type ErrorType { get { var errorType = new Entities.ErrorType(this); Populate(errorType); return errorType; } } /// /// Attempt to locate debugging information for a particular method. /// /// Returns null on failure, for example if there was no PDB information found for the /// DLL, or if the particular method is compiler generated or doesn't come from source code. /// /// The handle of the method. /// The debugging information, or null if the information could not be located. public PDB.Method? GetMethodDebugInformation(MethodDefinitionHandle handle) { return Pdb?.GetMethod(handle.ToDebugInformationHandle()); } } }