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