using System; using System.Collections.Generic; using System.IO; using System.Linq; 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. /// partial class Context : IDisposable { readonly FileStream stream; Entities.Assembly? assemblyNull; public Extraction.Context cx { get; } 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(Extraction.Context cx, string assemblyPath, bool extractPdbs) { this.cx = cx; 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 != null) { cx.Extractor.Logger.Log(Util.Logging.Severity.Info, string.Format("Found PDB information for {0}", assemblyPath)); } } } void IDisposable.Dispose() { if (pdb != 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("::"); } public readonly Entities.TypeSignatureDecoder TypeSignatureDecoder; /// /// 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.IMethod? GetMethodDebugInformation(MethodDefinitionHandle handle) { return pdb == null ? null : pdb.GetMethod(handle.ToDebugInformationHandle()); } } /// /// When we decode a type/method signature, we need access to /// generic parameters. /// public abstract class GenericContext { public Context cx; public GenericContext(Context cx) { this.cx = cx; } /// /// The list of generic type parameters, including type parameters of /// containing types. /// public abstract IEnumerable TypeParameters { get; } /// /// The list of generic method parameters. /// public abstract IEnumerable MethodParameters { get; } /// /// Gets the `p`th type parameter. /// /// The index of the parameter. /// /// For constructed types, the supplied type. /// For unbound types, the type parameter. /// public Entities.Type GetGenericTypeParameter(int p) { return TypeParameters.ElementAt(p); } /// /// Gets the `p`th method type parameter. /// /// The index of the parameter. /// /// For constructed types, the supplied type. /// For unbound types, the type parameter. /// public Entities.Type GetGenericMethodParameter(int p) { return MethodParameters.ElementAt(p); } } /// /// A generic context which does not contain any type parameters. /// public class EmptyContext : GenericContext { public EmptyContext(Context cx) : base(cx) { } public override IEnumerable TypeParameters { get { yield break; } } public override IEnumerable MethodParameters { get { yield break; } } } }