singrdk/base/Windows/mkmsil/msil/MetaDataResolver.cs

438 lines
16 KiB
C#
Raw Permalink Normal View History

2008-11-17 18:29:00 -05:00
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
namespace Bartok.MSIL
{
using System;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Text;
//
// MetaDataResolver.cs
//
// Facility for loading metadata from multiple assemblies and
// supporting resolution of references to definitions
public class MetaDataResolver {
// Use these constructors to load a set of assemblies (including code)
public MetaDataResolver(ArrayList loadFileNames, bool fLoadDebugInfo):
this(loadFileNames, System.DateTime.Now, fLoadDebugInfo)
{
}
public MetaDataResolver(ArrayList loadFileNames,
DateTime startTime,
bool fLoadDebugInfo)
{
this.startTime = startTime;
LoadMetaData(LoadStreams(loadFileNames, null), true, true, fLoadDebugInfo);
BuildMap();
}
// Use these constructor to create a resolver for library assemblies
public MetaDataResolver(ArrayList refFileNames,
ArrayList libDirNames,
bool fLoadDebugInfo):
this(refFileNames, libDirNames, System.DateTime.Now, fLoadDebugInfo)
{
}
public MetaDataResolver(ArrayList refFileNames,
ArrayList libDirNames,
DateTime startTime,
bool fLoadDebugInfo) {
this.startTime = startTime;
LoadMetaData(LoadStreams(refFileNames, libDirNames), false, false, fLoadDebugInfo);
BuildMap();
}
// Use these constructors to load a set of assemblies from streams.
public MetaDataResolver(ArrayList loadedStreams,
bool fLoadCode,
bool fLoadSectionsFromExecutable,
bool fLoadDebugInfo):
this(loadedStreams,
System.DateTime.Now,
fLoadSectionsFromExecutable,
fLoadDebugInfo,
fLoadDebugInfo)
{
}
public MetaDataResolver(ArrayList loadedStreams,
DateTime startTime,
bool fLoadCode,
bool fLoadSectionsFromExecutable,
bool fLoadDebugInfo)
{
this.startTime = startTime;
LoadMetaData(loadedStreams, fLoadCode, fLoadSectionsFromExecutable, fLoadDebugInfo);
BuildMap();
}
public MetaDataTypeDefinition ResolveTypeRef(MetaDataTypeReference reference) {
String refName = reference.Namespace+"."+reference.Name;
return ResolveName(refName);
}
public MetaDataTypeDefinition ResolveName(String refName) {
if (fDebug) {
Console.Out.WriteLine("Attempting to resolve in "+this.GetHashCode()+": "+refName);
}
EntryPair pair = (EntryPair)mapTable[refName];
if (pair == null) {
if (fDebug) {
Console.Out.WriteLine("...failed");
}
return null;
}
else {
MetaDataTypeDefinition result = pair.Definition;
if (fDebug) {
ResolverEntry entry = pair.Entry;
Console.Out.WriteLine("...found "+result+" in "+entry);
}
return result;
}
}
public ArrayList EntryList {
get { return entryList; }
}
public ArrayList MetaDataList {
get { return metaDataList; }
}
public override String ToString() {
StringBuilder sb = new StringBuilder("MetaDataResolver(");
foreach (Object element in this.entryList) {
sb.Append(element.ToString());
sb.Append(",");
}
sb.Append(")");
return sb.ToString();
}
//////////////////////////
private ArrayList LoadStreams(ArrayList refFileNames,
ArrayList libDirNames) {
// try to create streams for all files in refFileNames,
// looking first in the current directory, then (sequentially) in
// all the directories named in libDirNames
ArrayList streams = new ArrayList();
foreach (String fileName in refFileNames) {
Stream stream = null;
String successName = null;
if (fDebug) {
Console.Out.WriteLine("looking for '"+fileName+"'");
}
try {
stream = new FileStream(fileName,
FileMode.Open,
FileAccess.Read,
FileShare.Read);
successName = fileName;
}
catch (FileNotFoundException) {
// failed on raw filename; try prefixing with libDirNames elts
if (fDebug) {
Console.Out.WriteLine("...failed");
}
if (libDirNames != null) {
foreach (String dirName in libDirNames) {
try {
String fullName = dirName+"\\"+fileName;
if (fDebug) {
Console.Out.WriteLine("looking for '"+fullName+"'");
}
stream = new FileStream(fileName,
FileMode.Open,
FileAccess.Read,
FileShare.Read);
successName = fullName;
}
catch (FileNotFoundException) {
if (fDebug) {
Console.Out.WriteLine("...failed");
}
continue;
}
catch (DirectoryNotFoundException) {
if (fDebug) {
Console.Out.WriteLine("...failed");
}
continue;
}
}
}
}
StringBuilder sb = new StringBuilder();
if (successName == null) {
sb.Append("Unable to find '");
sb.Append(fileName);
sb.Append("' in current dir");
if (libDirNames != null && libDirNames.Count > 0) {
sb.Append(" or {");
foreach (String dirName in libDirNames) {
sb.Append(dirName);
sb.Append(", ");
}
sb.Remove(sb.Length-2, 2);
sb.Append("}");
}
Console.Out.WriteLine(sb.ToString());
Console.Out.WriteLine("Aborting!");
String stackTrace = System.Environment.StackTrace;
Console.Out.WriteLine(stackTrace);
throw new Exception(sb.ToString());
}
streams.Add(new LoadedStream(successName,
Path.GetFullPath(successName),
stream));
}
return streams;
}
private void LoadMetaData(ArrayList loadedStreams,
bool fLoadCode,
bool fLoadSectionsFromExecutable,
bool fLoadDebugInfo) {
// try to load the metadata for all streams in loadedStreams.
this.entryList = new ArrayList();
this.metaDataList = new ArrayList();
foreach (LoadedStream stream in loadedStreams) {
PELoader loader = null;
try {
if (fDebug) {
Console.Out.WriteLine("loading "+stream.Name+"'");
}
loader = new PELoader(stream.Content, stream.Name);
}
catch (FileNotFoundException) {
// failed on raw filename; try prefixing with libDirNames elts
if (fDebug) {
Console.Out.WriteLine("...failed");
}
}
StringBuilder sb = new StringBuilder();
if (loader == null) {
sb.Append("Unable to load '");
sb.Append(stream.Name);
sb.Append("'");
Console.Out.WriteLine(sb.ToString());
Console.Out.WriteLine("Aborting!");
String stackTrace = System.Environment.StackTrace;
Console.Out.WriteLine(stackTrace);
throw new Exception(sb.ToString());
}
TimeSpan diffSpan = System.DateTime.Now.Subtract(startTime);
int diffMsec = (int) diffSpan.TotalMilliseconds;
String secString = (diffMsec / 1000).ToString();
sb.Append(" ", 0, (8 - secString.Length));
sb.Append(secString);
sb.Append('.');
sb.Append((diffMsec%1000).ToString("000"));
sb.Append(": ");
sb.Append("Loading metadata ");
if (fLoadCode) {
sb.Append("with code ");
}
sb.Append("from '");
sb.Append(stream.Name);
sb.Append("'");
Console.Out.WriteLine(sb.ToString());
MetaData metaData =
MetaData.loadMetaData(stream.Path, loader, fLoadCode, fLoadDebugInfo);
metaDataList.Add(metaData);
ResolverEntry entry = new ResolverEntry(metaData, stream.Name);
entryList.Add(entry);
// if (fLoadSectionsFromExecutable && loader.IsExecutableImage()) {
// Console.Out.WriteLine("loading all sections from " + fileName);
// loader.loadSections();
// }
loader.getStream().Close();
}
}
private void BuildMap() {
// walk entryList in reverse, adding exports to mapTable
mapTable = new Hashtable();
for (int i = entryList.Count - 1; i >= 0; i--) {
ResolverEntry entry = (ResolverEntry)entryList[i];
MetaData metaData = entry.MetaData;
// export public typedefs
if (fDebug) {
Console.Out.WriteLine("Registering "+metaData.TypeDefs.Length+" public TypeDefs from '"+entry.Filename+"' in "+this.GetHashCode());
}
foreach (MetaDataTypeDefinition def in metaData.TypeDefs) {
TypeAttributes visibility =
def.Flags & TypeAttributes.VisibilityMask;
String fullName = def.FullName;
if ((visibility == TypeAttributes.Public) ||
(visibility == TypeAttributes.NestedPublic) ||
((visibility == TypeAttributes.NotPublic) &&
(fullName.StartsWith("System.Runtime.Compiler") ||
fullName.StartsWith("Microsoft.Win32.Win32Native")))) {
if (fDebug) {
Console.Out.WriteLine("Registering "+fullName);
}
mapTable[fullName] = new EntryPair(def, entry);
}
}
// export ExportedTypes
if (fDebug) {
Console.Out.WriteLine("Registering "+metaData.ExportedTypes.Length+" public ExportTypes from '"+entry.Filename+"'");
}
foreach (MetaDataExportedType exportType in metaData.ExportedTypes) {
String fullName = (exportType.Namespace.Length > 0)
? exportType.Namespace+"."+exportType.Name
: exportType.Name;
if (mapTable[fullName] == null) {
Console.Out.WriteLine("MetaDataResolver doesn't know how to handle "+fullName+"--"+exportType.ToStringLong());
throw new Exception();
}
Console.Out.WriteLine("WARNING: assuming that ExportedType "+exportType.ToStringLong()+" matches "+mapTable[fullName]);
}
}
}
public static void ResolveCustomAttributes(MetaDataResolver[] resolvers)
{
foreach (MetaDataResolver resolver in resolvers) {
resolver.ResolveMyCustomAttributes(resolvers);
}
}
public void ResolveMyCustomAttributes(MetaDataResolver[] resolvers)
{
for (int i = this.entryList.Count - 1; i >= 0; i--) {
ResolverEntry entry = (ResolverEntry) this.entryList[i];
MetaData metaData = entry.MetaData;
foreach (MetaDataCustomAttribute ca in metaData.CustomAttributes) {
// Console.WriteLine(ca.ToString());
ca.resolveReferences(this, resolvers);
// Console.WriteLine(ca.ToStringLong());
}
}
}
//////////////////////////
private static readonly bool fDebug = false;
private DateTime startTime;
private ArrayList entryList; // associates filenames with MetaData instances
private Hashtable mapTable; // map FullName to EntryPair(typeRef, resolverEntry)
private ArrayList metaDataList;
/////////////////////////////////////////
private class EntryPair {
public EntryPair(MetaDataTypeDefinition definition,
ResolverEntry entry) {
this.Definition = definition;
this.Entry = entry;
}
public readonly MetaDataTypeDefinition Definition;
public readonly ResolverEntry Entry;
public override String ToString() {
StringBuilder sb = new StringBuilder("EntryPair(");
sb.Append(Definition.ToString());
sb.Append(",");
sb.Append(Entry.ToString());
sb.Append(")");
return sb.ToString();
}
}
// descriptive info associated with resolved metadata objects
public class ResolverEntry {
public ResolverEntry(MetaData metaData, String filename) {
this.metaData = metaData;
this.filename = filename;
}
public MetaData MetaData {
get { return metaData; }
}
public String Filename {
get { return filename; }
}
public override String ToString() {
return ("<ResolverEntry '"+filename+"'>");
}
/////////////////////////////
private MetaData metaData;
private String filename;
}
public class LoadedStream
{
public LoadedStream(String name, String path, Stream stream)
{
this.name = name;
this.path = path;
this.stream = stream;
}
public LoadedStream(String name, Stream stream)
{
this.name = name;
this.path = name;
this.stream = stream;
}
public LoadedStream(Stream stream)
{
this.name = "<anonymous>";
this.path = "<anonymous>";
this.stream = stream;
}
public String Name {
get { return name; }
}
public String Path {
get { return path; }
}
public Stream Content {
get { return stream; }
}
public override String ToString() {
return ("LoadedStream("+name+":"+path+")");
}
private String name;
private String path;
private Stream stream;
}
}
}