mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 20:39:01 +00:00
3073 lines
142 KiB
C#
3073 lines
142 KiB
C#
#define VERBOSE_DEBUG
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Xml;
|
|
using Indy.IL2CPU.Assembler;
|
|
using Indy.IL2CPU.Assembler.X86;
|
|
using Indy.IL2CPU.IL;
|
|
using Indy.IL2CPU.Plugs;
|
|
using System.Runtime.InteropServices;
|
|
using System.Xml.Serialization;
|
|
using System.Collections.ObjectModel;
|
|
using System.Diagnostics.SymbolStore;
|
|
using Microsoft.Samples.Debugging.CorSymbolStore;
|
|
using System.Threading;
|
|
using System.Diagnostics;
|
|
|
|
namespace Indy.IL2CPU
|
|
{
|
|
public enum DebugMode { None, IL, Source, MLUsingGDB }
|
|
|
|
public class MethodBaseComparer : IComparer<MethodBase>
|
|
{
|
|
#region IComparer<MethodBase> Members
|
|
|
|
public int Compare(MethodBase x,
|
|
MethodBase y)
|
|
{
|
|
return x.GetFullName().CompareTo(y.GetFullName());
|
|
//return x.Name.CompareTo(y.Name);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
|
|
public class StringMethodBaseComparer : IComparer<string>
|
|
{
|
|
#region IComparer<MethodBase> Members
|
|
|
|
public int Compare(string x,
|
|
string y)
|
|
{
|
|
return x.CompareTo(y);
|
|
//return x.Name.CompareTo(y.Name);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
public class FieldInfoComparer : IComparer<FieldInfo>
|
|
{
|
|
#region IComparer<FieldInfo> Members
|
|
|
|
public int Compare(FieldInfo x,
|
|
FieldInfo y)
|
|
{
|
|
return x.GetFullName().CompareTo(y.GetFullName());
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
public class TypeComparer : IComparer<Type>
|
|
{
|
|
public int Compare(Type x,
|
|
Type y)
|
|
{
|
|
return x.AssemblyQualifiedName.CompareTo(y.AssemblyQualifiedName);
|
|
}
|
|
}
|
|
|
|
public class TypeEqualityComparer : IEqualityComparer<Type>
|
|
{
|
|
public bool Equals(Type x,
|
|
Type y)
|
|
{
|
|
return x.FullName.Equals(y.FullName);
|
|
}
|
|
|
|
public int GetHashCode(Type obj)
|
|
{
|
|
return obj.FullName.GetHashCode();
|
|
}
|
|
}
|
|
|
|
public class AssemblyEqualityComparer : IEqualityComparer<Assembly>
|
|
{
|
|
public bool Equals(Assembly x,
|
|
Assembly y)
|
|
{
|
|
return x.GetName().FullName.Equals(y.GetName().FullName);
|
|
}
|
|
|
|
public int GetHashCode(Assembly obj)
|
|
{
|
|
return obj.GetName().FullName.GetHashCode();
|
|
}
|
|
}
|
|
|
|
public enum LogSeverityEnum : byte { Warning = 0, Error = 1, Informational = 2, Performance = 3 }
|
|
|
|
public delegate void DebugLogHandler(LogSeverityEnum aSeverity, string aMessage);
|
|
|
|
public enum TargetPlatformEnum { X86 }
|
|
|
|
public enum TraceAssemblies { All, Cosmos, User };
|
|
|
|
public class QueuedMethodInformation
|
|
{
|
|
public bool Processed;
|
|
public bool PreProcessed;
|
|
public int Index;
|
|
public MLDebugSymbol[] Instructions;
|
|
public readonly SortedList<string, object> Info = new SortedList<string, object>(StringComparer.InvariantCultureIgnoreCase);
|
|
public MethodBase Implementation;
|
|
public string FullName;
|
|
}
|
|
|
|
public class QueuedStaticFieldInformation
|
|
{
|
|
public bool Processed;
|
|
}
|
|
|
|
public class Engine
|
|
{
|
|
protected static Engine mCurrent;
|
|
protected DebugLogHandler mDebugLog;
|
|
protected OpCodeMap mMap;
|
|
protected Assembler.Assembler mAssembler;
|
|
public TraceAssemblies TraceAssemblies { get; set; }
|
|
|
|
private SortedList<string, MethodBase> mPlugMethods;
|
|
private SortedList<Type, Dictionary<string, PlugFieldAttribute>> mPlugFields;
|
|
|
|
/// <summary>
|
|
/// Contains a list of all methods. This includes methods to be processed and already processed.
|
|
/// </summary>
|
|
protected IDictionary<MethodBase, QueuedMethodInformation> mMethods = new SortedList<MethodBase, QueuedMethodInformation>(new MethodBaseComparer());
|
|
protected ReaderWriterLocker mMethodsLocker = new ReaderWriterLocker();
|
|
|
|
/// <summary>
|
|
/// Contains a list of all static fields. This includes static fields to be processed and already processed.
|
|
/// </summary>
|
|
protected IDictionary<FieldInfo, QueuedStaticFieldInformation> mStaticFields = new SortedList<FieldInfo, QueuedStaticFieldInformation>(new FieldInfoComparer());
|
|
protected ReaderWriterLocker mStaticFieldsLocker = new ReaderWriterLocker();
|
|
protected IList<Type> mTypes = new List<Type>();
|
|
protected ReaderWriterLocker mTypesLocker = new ReaderWriterLocker();
|
|
protected TypeEqualityComparer mTypesEqualityComparer = new TypeEqualityComparer();
|
|
private byte mDebugComport;
|
|
private DebugMode mDebugMode;
|
|
private List<MLDebugSymbol> mSymbols = new List<MLDebugSymbol>();
|
|
private ReaderWriterLocker mSymbolsLocker = new ReaderWriterLocker();
|
|
private string mOutputDir;
|
|
public event Action<string> ChangingCurrentMethod;
|
|
public event Action<string> ChangingInnerMethod;
|
|
public event Action<int, int> CompilingMethods;
|
|
public event Action<int, int> CompilingStaticFields;
|
|
|
|
/// <summary>
|
|
/// Compiles an assembly to CPU-specific code. The entrypoint of the assembly will be
|
|
/// crawled to see what is neccessary, same goes for all dependencies.
|
|
/// </summary>
|
|
/// <remarks>For now, only entrypoints without params are supported!</remarks>
|
|
/// <param name="aAssembly">The assembly of which to crawl the entry-point method.</param>
|
|
/// <param name="aTargetPlatform">The platform to target when assembling the code.</param>
|
|
/// <param name="aOutput"></param>
|
|
public void Simulate(IEnumerable<string> aPlugs)
|
|
{
|
|
mMap = new Indy.IL2CPU.IL.X86.X86OpCodeMap();
|
|
mAssembler = new Assembler.X86.CosmosAssembler((byte)0);
|
|
InitializePlugs(aPlugs);
|
|
ScanAllMethods();
|
|
}
|
|
|
|
//TODO: Way too many params, these should be properties
|
|
public void Execute(string aAssembly,
|
|
TargetPlatformEnum aTargetPlatform,
|
|
Func<string, string> aGetFileNameForGroup,
|
|
IEnumerable<string> aPlugs,
|
|
DebugMode aDebugMode,
|
|
bool aGDBDebug,
|
|
byte aDebugComNumber,
|
|
string aOutputDir,
|
|
bool aUseBinaryEmission)
|
|
{
|
|
Assembly mCrawledAssembly;
|
|
|
|
|
|
mCurrent = this;
|
|
try
|
|
{
|
|
if (aGetFileNameForGroup == null)
|
|
{
|
|
throw new ArgumentNullException("aGetFileNameForGroup");
|
|
}
|
|
mCrawledAssembly = Assembly.LoadFrom(aAssembly);
|
|
mDebugMode = aDebugMode;
|
|
MethodInfo xEntryPoint = (MethodInfo)mCrawledAssembly.EntryPoint;
|
|
if (xEntryPoint == null)
|
|
{
|
|
throw new NotSupportedException("No EntryPoint found!");
|
|
}
|
|
mOutputDir = aOutputDir;
|
|
|
|
Type xEntryPointType = xEntryPoint.DeclaringType;
|
|
xEntryPoint = xEntryPointType.GetMethod("Init", new Type[0]);
|
|
mDebugComport = aDebugComNumber;
|
|
AppDomainSetup xAppDomainSetup = new AppDomainSetup();
|
|
//xAppDomainSetup.PrivateBinPath=
|
|
//AppDomain.CurrentDomain.AppendPrivatePath(Path.GetDirectoryName(mCrawledAssembly.Location));
|
|
//List<string> xSearchDirs = new List<string>(new string[] { Path.GetDirectoryName(aAssembly), aAssemblyDir });
|
|
//xSearchDirs.AddRange((from item in aPlugs
|
|
// select Path.GetDirectoryName(item)).Distinct());
|
|
switch (aTargetPlatform)
|
|
{
|
|
case TargetPlatformEnum.X86:
|
|
{
|
|
mMap = new Indy.IL2CPU.IL.X86.X86OpCodeMap();
|
|
mAssembler = new Assembler.X86.CosmosAssembler(((aDebugMode != DebugMode.None) && (aDebugMode != DebugMode.MLUsingGDB))
|
|
? aDebugComNumber
|
|
: (byte)0);
|
|
break;
|
|
}
|
|
default:
|
|
throw new NotSupportedException("TargetPlatform '" + aTargetPlatform + "' not supported!");
|
|
}
|
|
InitializePlugs(aPlugs);
|
|
using (mAssembler)
|
|
{
|
|
mAssembler.Initialize();
|
|
//mAssembler.OutputType = Assembler.Win32.Assembler.OutputTypeEnum.Console;
|
|
//foreach (string xPlug in aPlugs) {
|
|
//this.I
|
|
List<Assembly> xAppDefs = new List<Assembly>();
|
|
xAppDefs.Add(mCrawledAssembly);
|
|
// AssemblyEqualityComparer xComparer = new AssemblyEqualityComparer();
|
|
foreach (Assembly xAsm in AppDomain.CurrentDomain.GetAssemblies())
|
|
{
|
|
Assembly xAssemblyDef = Assembly.LoadFrom(xAsm.Location);
|
|
if (!xAppDefs.Contains(xAssemblyDef))
|
|
{
|
|
xAppDefs.Add(xAssemblyDef);
|
|
}
|
|
}
|
|
mMap.Initialize(mAssembler, xAppDefs);
|
|
//!String.IsNullOrEmpty(aDebugSymbols);
|
|
IL.Op.QueueMethod += QueueMethod;
|
|
IL.Op.QueueStaticField += QueueStaticField;
|
|
try
|
|
{
|
|
using (mTypesLocker.AcquireWriterLock())
|
|
{
|
|
mTypes.Add(typeof(object));
|
|
}
|
|
using (mMethodsLocker.AcquireWriterLock())
|
|
{
|
|
mMethods.Add(RuntimeEngineRefs.InitializeApplicationRef,
|
|
new QueuedMethodInformation()
|
|
{
|
|
Processed = false,
|
|
Index = mMethods.Count
|
|
});
|
|
mMethods.Add(RuntimeEngineRefs.FinalizeApplicationRef,
|
|
new QueuedMethodInformation()
|
|
{
|
|
Processed = false,
|
|
Index = mMethods.Count
|
|
});
|
|
mMethods.Add(typeof(Assembler.Assembler).GetMethod("PrintException"),
|
|
new QueuedMethodInformation()
|
|
{
|
|
Index = mMethods.Count
|
|
});
|
|
mMethods.Add(VTablesImplRefs.LoadTypeTableRef,
|
|
new QueuedMethodInformation()
|
|
{
|
|
Processed = false,
|
|
Index = mMethods.Count
|
|
});
|
|
mMethods.Add(VTablesImplRefs.SetMethodInfoRef,
|
|
new QueuedMethodInformation()
|
|
{
|
|
Processed = false,
|
|
Index = mMethods.Count
|
|
});
|
|
mMethods.Add(VTablesImplRefs.IsInstanceRef,
|
|
new QueuedMethodInformation()
|
|
{
|
|
Processed = false,
|
|
Index = mMethods.Count
|
|
});
|
|
mMethods.Add(VTablesImplRefs.SetTypeInfoRef,
|
|
new QueuedMethodInformation()
|
|
{
|
|
Processed = false,
|
|
Index = mMethods.Count
|
|
});
|
|
mMethods.Add(VTablesImplRefs.GetMethodAddressForTypeRef,
|
|
new QueuedMethodInformation()
|
|
{
|
|
Processed = false,
|
|
Index = mMethods.Count
|
|
});
|
|
mMethods.Add(GCImplementationRefs.IncRefCountRef,
|
|
new QueuedMethodInformation()
|
|
{
|
|
Processed = false,
|
|
Index = mMethods.Count
|
|
});
|
|
mMethods.Add(GCImplementationRefs.DecRefCountRef,
|
|
new QueuedMethodInformation()
|
|
{
|
|
Processed = false,
|
|
Index = mMethods.Count
|
|
});
|
|
mMethods.Add(GCImplementationRefs.AllocNewObjectRef,
|
|
new QueuedMethodInformation()
|
|
{
|
|
Processed = false,
|
|
Index = mMethods.Count
|
|
});
|
|
mMethods.Add(xEntryPoint,
|
|
new QueuedMethodInformation()
|
|
{
|
|
Processed = false,
|
|
Index = mMethods.Count
|
|
});
|
|
}
|
|
ScanAllMethods();
|
|
ScanAllStaticFields();
|
|
mMap.PreProcess(mAssembler);
|
|
do
|
|
{
|
|
int xOldCount;
|
|
using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
xOldCount = mMethods.Count;
|
|
}
|
|
ScanAllMethods();
|
|
ScanAllStaticFields();
|
|
ScanForMethodsToIncludeForVMT();
|
|
int xNewCount;
|
|
using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
xNewCount = mMethods.Count;
|
|
}
|
|
if (xOldCount == xNewCount)
|
|
{
|
|
break;
|
|
}
|
|
} while (true);
|
|
// initialize the runtime engine
|
|
MainEntryPointOp xEntryPointOp = (MainEntryPointOp)GetOpFromType(mMap.MainEntryPointOp,
|
|
null,
|
|
null);
|
|
xEntryPointOp.Assembler = mAssembler;
|
|
xEntryPointOp.Enter(Assembler.Assembler.EntryPointName);
|
|
xEntryPointOp.Call(RuntimeEngineRefs.InitializeApplicationRef);
|
|
xEntryPointOp.Call("____INIT__VMT____");
|
|
using (mTypesLocker.AcquireWriterLock())
|
|
{
|
|
foreach (Type xType in mTypes)
|
|
{
|
|
foreach (MethodBase xMethod in xType.GetConstructors(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
|
|
{
|
|
if (xMethod.IsStatic)
|
|
{
|
|
xEntryPointOp.Call(xMethod);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
xEntryPointOp.Call(xEntryPoint);
|
|
if (xEntryPoint.ReturnType == typeof(void))
|
|
{
|
|
xEntryPointOp.Push(0);
|
|
}
|
|
// todo: implement support for returncodes?
|
|
xEntryPointOp.Call(RuntimeEngineRefs.FinalizeApplicationRef);
|
|
xEntryPointOp.Exit();
|
|
using (mMethodsLocker.AcquireWriterLock())
|
|
{
|
|
mMethods = new ReadOnlyDictionary<MethodBase, QueuedMethodInformation>(mMethods);
|
|
}
|
|
using (mStaticFieldsLocker.AcquireWriterLock())
|
|
{
|
|
mStaticFields = new ReadOnlyDictionary<FieldInfo, QueuedStaticFieldInformation>(mStaticFields);
|
|
}
|
|
ProcessAllMethods();
|
|
mMap.PostProcess(mAssembler);
|
|
ProcessAllStaticFields();
|
|
GenerateVMT(mDebugMode != DebugMode.None);
|
|
using (mSymbolsLocker.AcquireReaderLock())
|
|
{
|
|
if (mSymbols != null)
|
|
{
|
|
string xOutputFile = Path.Combine(mOutputDir, "debug.cxdb");
|
|
MLDebugSymbol.WriteSymbolsListToFile(mSymbols, xOutputFile);
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (aUseBinaryEmission)
|
|
{
|
|
using (Stream xOutStream = new FileStream(Path.Combine(aOutputDir, "output.bin"), FileMode.Create))
|
|
{
|
|
Stopwatch xSW = new Stopwatch();
|
|
xSW.Start();
|
|
try
|
|
{
|
|
mAssembler.FlushBinary(xOutStream, 0x200000);
|
|
}
|
|
finally
|
|
{
|
|
xSW.Stop();
|
|
Debug.WriteLine(String.Format("Binary Emission took: {0}", xSW.Elapsed));
|
|
}
|
|
}
|
|
}
|
|
else // use external build
|
|
{
|
|
using (StreamWriter xOutput = new StreamWriter(aGetFileNameForGroup("main")))
|
|
{
|
|
mAssembler.FlushText(xOutput);
|
|
}
|
|
}
|
|
IL.Op.QueueMethod -= QueueMethod;
|
|
IL.Op.QueueStaticField -= QueueStaticField;
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
mCurrent = null;
|
|
}
|
|
}
|
|
|
|
// EDIT BELOW TO CHANGE THREAD COUNT:
|
|
private int mThreadCount = 1;// Environment.ProcessorCount;
|
|
private AutoResetEvent[] mThreadEvents = new AutoResetEvent[1];//new AutoResetEvent[mThreadCount];
|
|
|
|
private void ScanAllMethods()
|
|
{
|
|
if (mThreadCount == 1)
|
|
{
|
|
DoScanMethodsNoThreading();
|
|
return;
|
|
}
|
|
//Doku: work with a thread if someone is monitoring
|
|
if (ChangingCurrentMethod != null && ChangingInnerMethod != null)
|
|
{
|
|
DoScanMethods(0);
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < mThreadCount; i++)
|
|
{
|
|
mThreadEvents[i] = new AutoResetEvent(false);
|
|
var xThread = new Thread(DoScanMethods);
|
|
xThread.Start(i);
|
|
}
|
|
int xFinishedThreads = 0;
|
|
while (xFinishedThreads < mThreadCount)
|
|
{
|
|
for (int i = 0; i < mThreadCount; i++)
|
|
{
|
|
if (mThreadEvents[i] != null)
|
|
{
|
|
//HACK takes a while
|
|
// if (mThreadEvents[i].WaitOne(10, false))
|
|
{
|
|
mThreadEvents[i].Close();
|
|
mThreadEvents[i] = null;
|
|
xFinishedThreads++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//UNThreaded
|
|
private void DoScanMethodsNoThreading()
|
|
{
|
|
//ProgressChanged.Invoke("Scanning methods");
|
|
|
|
int xIndex = -1;
|
|
MethodBase xCurrentMethod;
|
|
while (true)
|
|
{
|
|
xIndex++;
|
|
// using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
xCurrentMethod = (from item in mMethods.Keys
|
|
where !mMethods[item].PreProcessed
|
|
select item).FirstOrDefault();
|
|
}
|
|
if (xCurrentMethod == null)
|
|
{
|
|
break;
|
|
}
|
|
if (ChangingCurrentMethod != null)
|
|
ChangingCurrentMethod.Invoke(xCurrentMethod.GetFullName());
|
|
//ProgressChanged.Invoke(String.Format("Scanning method: {0}", xCurrentMethod.GetFullName()));
|
|
EmitDependencyGraphLine(true, xCurrentMethod.GetFullName());
|
|
try
|
|
{
|
|
RegisterType(xCurrentMethod.DeclaringType);
|
|
// using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
mMethods[xCurrentMethod].PreProcessed = true;
|
|
}
|
|
if (xCurrentMethod.IsAbstract)
|
|
{
|
|
continue;
|
|
}
|
|
string xMethodName = Label.GenerateLabelName(xCurrentMethod);
|
|
TypeInformation xTypeInfo = null;
|
|
if (!xCurrentMethod.IsStatic)
|
|
{
|
|
xTypeInfo = GetTypeInfo(xCurrentMethod.DeclaringType);
|
|
}
|
|
MethodInformation xMethodInfo;
|
|
// using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
xMethodInfo = GetMethodInfo(xCurrentMethod,
|
|
xCurrentMethod,
|
|
xMethodName,
|
|
xTypeInfo,
|
|
mDebugMode != DebugMode.None,
|
|
mMethods[xCurrentMethod].Info);
|
|
}
|
|
MethodBase xCustomImplementation = GetCustomMethodImplementation(xMethodName);
|
|
if (xCustomImplementation != null)
|
|
{
|
|
try
|
|
{
|
|
QueueMethod(xCustomImplementation);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new Exception("Method " + xCurrentMethod.GetFullName() + " has called " + e.Message + "! Probably it needs to be plugged");
|
|
}
|
|
// using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
mMethods[xCurrentMethod].Implementation = xCustomImplementation;
|
|
}
|
|
continue;
|
|
}
|
|
Type xOpType = mMap.GetOpForCustomMethodImplementation(xMethodName);
|
|
if (xOpType != null)
|
|
{
|
|
Op xMethodOp = GetOpFromType(xOpType, null, xMethodInfo);
|
|
if (xMethodOp != null)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
if (mMap.HasCustomAssembleImplementation(xMethodInfo))
|
|
{
|
|
mMap.ScanCustomAssembleImplementation(xMethodInfo);
|
|
continue;
|
|
}
|
|
|
|
//xCurrentMethod.GetMethodImplementationFlags() == MethodImplAttributes.
|
|
ILReader xReader = new ILReader(xCurrentMethod);
|
|
MethodBody xBody = xCurrentMethod.GetMethodBody();
|
|
// todo: add better detection of implementation state
|
|
|
|
if (xBody != null)
|
|
{
|
|
mInstructionsToSkip = 0;
|
|
mAssembler.StackContents.Clear();
|
|
|
|
var xInstructionInfos = new List<DebugSymbolsAssemblyTypeMethodInstruction>();
|
|
while (xReader.Read())
|
|
{
|
|
SortedList<string, object> xInfo = null;
|
|
// using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
xInfo = mMethods[xCurrentMethod].Info;
|
|
}
|
|
mMap.ScanILCode(xReader, xMethodInfo, xInfo);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
OnDebugLog(LogSeverityEnum.Error, xCurrentMethod.GetFullName());
|
|
OnDebugLog(LogSeverityEnum.Warning, e.ToString());
|
|
throw;
|
|
}
|
|
}
|
|
|
|
foreach (Type xType in mTypes)
|
|
{
|
|
foreach (MethodBase xMethod in xType.GetConstructors(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
|
|
{
|
|
if (xMethod.IsStatic)
|
|
{
|
|
try
|
|
{
|
|
QueueMethod(xMethod);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new Exception("Method " + xCurrentMethod.GetFullName() + " has called " + e.Message + "! Probably it needs to be plugged");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
private void DoScanMethods(object data)
|
|
{
|
|
//ProgressChanged.Invoke("Scanning methods");
|
|
int xThreadIndex = (int)data;
|
|
try
|
|
{
|
|
int xIndex = -1;
|
|
MethodBase xCurrentMethod;
|
|
while (true)
|
|
{
|
|
xIndex++;
|
|
if ((xIndex % mThreadCount) != xThreadIndex)
|
|
{
|
|
continue;
|
|
}
|
|
using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
xCurrentMethod = (from item in mMethods.Keys
|
|
where !mMethods[item].PreProcessed
|
|
select item).FirstOrDefault();
|
|
}
|
|
if (xCurrentMethod == null)
|
|
{
|
|
break;
|
|
}
|
|
if (ChangingCurrentMethod != null)
|
|
ChangingCurrentMethod.Invoke(xCurrentMethod.GetFullName());
|
|
//ProgressChanged.Invoke(String.Format("Scanning method: {0}", xCurrentMethod.GetFullName()));
|
|
EmitDependencyGraphLine(true, xCurrentMethod.GetFullName());
|
|
try
|
|
{
|
|
RegisterType(xCurrentMethod.DeclaringType);
|
|
using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
mMethods[xCurrentMethod].PreProcessed = true;
|
|
}
|
|
if (xCurrentMethod.IsAbstract)
|
|
{
|
|
continue;
|
|
}
|
|
string xMethodName = Label.GenerateLabelName(xCurrentMethod);
|
|
TypeInformation xTypeInfo = null;
|
|
if (!xCurrentMethod.IsStatic)
|
|
{
|
|
xTypeInfo = GetTypeInfo(xCurrentMethod.DeclaringType);
|
|
}
|
|
MethodInformation xMethodInfo;
|
|
using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
xMethodInfo = GetMethodInfo(xCurrentMethod,
|
|
xCurrentMethod,
|
|
xMethodName,
|
|
xTypeInfo,
|
|
mDebugMode != DebugMode.None,
|
|
mMethods[xCurrentMethod].Info);
|
|
}
|
|
MethodBase xCustomImplementation = GetCustomMethodImplementation(xMethodName);
|
|
if (xCustomImplementation != null)
|
|
{
|
|
try
|
|
{
|
|
QueueMethod(xCustomImplementation);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new Exception("Method " + xCurrentMethod.GetFullName() + " has called " + e.Message + "! Probably it needs to be plugged");
|
|
}
|
|
using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
mMethods[xCurrentMethod].Implementation = xCustomImplementation;
|
|
}
|
|
continue;
|
|
}
|
|
Type xOpType = mMap.GetOpForCustomMethodImplementation(xMethodName);
|
|
if (xOpType != null)
|
|
{
|
|
Op xMethodOp = GetOpFromType(xOpType, null, xMethodInfo);
|
|
if (xMethodOp != null)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
if (mMap.HasCustomAssembleImplementation(xMethodInfo))
|
|
{
|
|
mMap.ScanCustomAssembleImplementation(xMethodInfo);
|
|
continue;
|
|
}
|
|
|
|
//xCurrentMethod.GetMethodImplementationFlags() == MethodImplAttributes.
|
|
MethodBody xBody = xCurrentMethod.GetMethodBody();
|
|
// todo: add better detection of implementation state
|
|
if (xBody != null)
|
|
{
|
|
mInstructionsToSkip = 0;
|
|
mAssembler.StackContents.Clear();
|
|
ILReader xReader = new ILReader(xCurrentMethod);
|
|
var xInstructionInfos = new List<DebugSymbolsAssemblyTypeMethodInstruction>();
|
|
while (xReader.Read())
|
|
{
|
|
SortedList<string, object> xInfo = null;
|
|
using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
xInfo = mMethods[xCurrentMethod].Info;
|
|
}
|
|
mMap.ScanILCode(xReader, xMethodInfo, xInfo);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
OnDebugLog(LogSeverityEnum.Error, xCurrentMethod.GetFullName());
|
|
OnDebugLog(LogSeverityEnum.Warning, e.ToString());
|
|
throw;
|
|
}
|
|
}
|
|
using (mTypesLocker.AcquireReaderLock())
|
|
{
|
|
foreach (Type xType in mTypes)
|
|
{
|
|
foreach (MethodBase xMethod in xType.GetConstructors(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
|
|
{
|
|
if (xMethod.IsStatic)
|
|
{
|
|
try
|
|
{
|
|
QueueMethod(xMethod);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new Exception("Method " + xCurrentMethod.GetFullName() + " has called " + e.Message + "! Probably it needs to be plugged");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
mThreadEvents[xThreadIndex].Set();
|
|
}
|
|
}
|
|
|
|
private void ScanAllStaticFields()
|
|
{
|
|
}
|
|
|
|
private void GenerateDebugSymbols()
|
|
{
|
|
/*var xAssemblyComparer = new AssemblyEqualityComparer();
|
|
var xTypeComparer = new TypeEqualityComparer();
|
|
var xDbgAssemblies = new List<DebugSymbolsAssembly>();
|
|
int xTypeCount = mTypes.Count;
|
|
try {
|
|
foreach (var xAssembly in (from item in mTypes
|
|
select item.Assembly).Distinct(xAssemblyComparer)) {
|
|
var xDbgAssembly = new DebugSymbolsAssembly();
|
|
var xDbgAssemblyTypes = new List<DebugSymbolsAssemblyType>();
|
|
xDbgAssembly.FileName = xAssembly.Location;
|
|
xDbgAssembly.FullName = xAssembly.GetName().FullName;
|
|
//if (xDbgAssembly.FullName == "Cosmos.Hardware, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5ae71220097cb983") {
|
|
// System.Diagnostics.Debugger.Break();
|
|
//}
|
|
for (int xIdxTypes = 0; xIdxTypes < mTypes.Count; xIdxTypes++) {
|
|
var xType = mTypes[xIdxTypes];
|
|
if (!xAssemblyComparer.Equals(xAssembly, xType.Assembly)) {
|
|
continue;
|
|
}
|
|
var xDbgType = new DebugSymbolsAssemblyType();
|
|
//if (xType.FullName == "Cosmos.Hardware.Screen.Text") {
|
|
// System.Diagnostics.Debugger.Break();
|
|
//}
|
|
if (xType.BaseType != null) {
|
|
xDbgType.BaseTypeId = GetTypeId(xType.BaseType);
|
|
}
|
|
xDbgType.TypeId = xIdxTypes;
|
|
xDbgType.FullName = xType.FullName;
|
|
var xTypeFields = new List<DebugSymbolsAssemblyTypeField>();
|
|
var xTypeInfo = GetTypeInfo(xType);
|
|
xDbgType.StorageSize = GetFieldStorageSize(xType);
|
|
foreach (var xField in xType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)) {
|
|
var xDbgField = new DebugSymbolsAssemblyTypeField();
|
|
xDbgField.Name = xField.Name;
|
|
xDbgField.IsStatic = xField.IsStatic;
|
|
if (xField.IsPublic) {
|
|
xDbgField.Visibility = "Public";
|
|
} else {
|
|
if (xField.IsPrivate) {
|
|
xDbgField.Visibility = "Private";
|
|
} else {
|
|
if (xField.IsFamily) {
|
|
xDbgField.Visibility = "Protected";
|
|
} else {
|
|
xDbgField.Visibility = "Internal";
|
|
}
|
|
}
|
|
}
|
|
xDbgField.FieldType = GetTypeId(xField.FieldType);
|
|
if (xDbgField.IsStatic) {
|
|
xDbgField.Address = DataMember.GetStaticFieldName(xField);
|
|
} else {
|
|
xDbgField.Address = "+" + xTypeInfo.Fields[xField.GetFullName()].Offset;
|
|
}
|
|
xTypeFields.Add(xDbgField);
|
|
}
|
|
xDbgType.Field = xTypeFields.ToArray();
|
|
var xTypeMethods = new List<DebugSymbolsAssemblyTypeMethod>();
|
|
foreach (var xMethod in xType.GetMethods(BindingFlags.ExactBinding | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).Cast<MethodBase>().Union(xType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))) {
|
|
var xIdxMethods = mMethods.IndexOfKey(xMethod);
|
|
if (xIdxMethods == -1) {
|
|
continue;
|
|
}
|
|
//var xMethod = mMethods.Keys[xIdxMethods];
|
|
//if (!xTypeComparer.Equals(xMethod.DeclaringType, xType)) {
|
|
// continue;
|
|
//}
|
|
var xDbgMethod = new DebugSymbolsAssemblyTypeMethod();
|
|
xDbgMethod.Name = xMethod.Name;
|
|
xDbgMethod.MethodId = xIdxMethods;
|
|
xDbgMethod.Address = Label.GenerateLabelName(xMethod);
|
|
if (xMethod is ConstructorInfo) {
|
|
xDbgMethod.ReturnTypeId = GetTypeId(typeof(void));
|
|
} else {
|
|
var xTheMethod = xMethod as MethodInfo;
|
|
if (xTheMethod != null) {
|
|
xDbgMethod.ReturnTypeId = GetTypeId(xTheMethod.ReturnType);
|
|
} else {
|
|
xDbgMethod.ReturnTypeId = GetTypeId(typeof(void));
|
|
}
|
|
}
|
|
if (xMethod.IsPublic) {
|
|
xDbgMethod.Visibility = "Public";
|
|
} else {
|
|
if (xMethod.IsPrivate) {
|
|
xDbgMethod.Visibility = "Private";
|
|
} else {
|
|
if (xMethod.IsFamily) {
|
|
xDbgMethod.Visibility = "Protected";
|
|
} else {
|
|
xDbgMethod.Visibility = "Internal";
|
|
}
|
|
}
|
|
}
|
|
xTypeMethods.Add(xDbgMethod);
|
|
MethodBody xBody = xMethod.GetMethodBody();
|
|
if (xBody != null) {
|
|
var xDbgLocals = new List<DebugSymbolsAssemblyTypeMethodLocal>();
|
|
var xMethodInfo = GetMethodInfo(xMethod, xMethod, Label.GenerateLabelName(xMethod), xTypeInfo);
|
|
if (xBody.LocalVariables != null) {
|
|
foreach (var xLocal in xBody.LocalVariables) {
|
|
var xDbgLocal = new DebugSymbolsAssemblyTypeMethodLocal();
|
|
xDbgLocal.Name = xLocal.LocalIndex.ToString();
|
|
xDbgLocal.LocalTypeId = GetTypeId(xLocal.LocalType);
|
|
xDbgLocal.RelativeStartAddress = xMethodInfo.Locals[xLocal.LocalIndex].VirtualAddresses.First();
|
|
xDbgLocals.Add(xDbgLocal);
|
|
}
|
|
}
|
|
xDbgMethod.Local = xDbgLocals.ToArray();
|
|
}
|
|
xDbgMethod.Body = mMethods.Values[xIdxMethods].Instructions;
|
|
}
|
|
xDbgType.Method = xTypeMethods.ToArray();
|
|
xDbgAssemblyTypes.Add(xDbgType);
|
|
}
|
|
xDbgAssembly.Type = xDbgAssemblyTypes.ToArray();
|
|
xDbgAssemblies.Add(xDbgAssembly);
|
|
}
|
|
} finally {
|
|
if (xTypeCount != mTypes.Count) {
|
|
Console.WriteLine("TypeCount changed (was {0}, new {1})", xTypeCount, mTypes.Count);
|
|
Console.WriteLine("Last Type: {0}", mTypes.Last().FullName);
|
|
}
|
|
}*/
|
|
}
|
|
|
|
private void GenerateVMT(bool aDebugMode)
|
|
{
|
|
Op xOp = GetOpFromType(mMap.MethodHeaderOp,
|
|
null,
|
|
new MethodInformation("____INIT__VMT____",
|
|
new MethodInformation.Variable[0],
|
|
new MethodInformation.Argument[0],
|
|
0,
|
|
false,
|
|
null,
|
|
null,
|
|
typeof(void),
|
|
aDebugMode,
|
|
new Dictionary<string, object>()));
|
|
xOp.Assembler = mAssembler;
|
|
xOp.Assemble();
|
|
InitVmtImplementationOp xInitVmtOp = (InitVmtImplementationOp)GetOpFromType(mMap.InitVmtImplementationOp,
|
|
null,
|
|
null);
|
|
xInitVmtOp.Assembler = mAssembler;
|
|
xInitVmtOp.Types = mTypes;
|
|
xInitVmtOp.SetTypeInfoRef = VTablesImplRefs.SetTypeInfoRef;
|
|
xInitVmtOp.SetMethodInfoRef = VTablesImplRefs.SetMethodInfoRef;
|
|
xInitVmtOp.LoadTypeTableRef = VTablesImplRefs.LoadTypeTableRef;
|
|
xInitVmtOp.TypesFieldRef = VTablesImplRefs.VTablesImplDef.GetField("mTypes",
|
|
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
|
|
using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
xInitVmtOp.Methods = mMethods.Keys.ToList();
|
|
}
|
|
xInitVmtOp.VTableEntrySize = GetFieldStorageSize(GetType("",
|
|
typeof(VTable).FullName.Replace('+',
|
|
'.')));
|
|
xInitVmtOp.GetMethodIdentifier += delegate(MethodBase aMethod)
|
|
{
|
|
if (aMethod.GetFullName() == "System.Reflection.Cache.InternalCache System.Reflection.MemberInfo.get_Cache()")
|
|
{
|
|
System.Diagnostics.Debugger.Break();
|
|
}
|
|
ParameterInfo[] xParams = aMethod.GetParameters();
|
|
Type[] xParamTypes = new Type[xParams.Length];
|
|
for (int i = 0; i < xParams.Length; i++)
|
|
{
|
|
xParamTypes[i] = xParams[i].ParameterType;
|
|
}
|
|
MethodBase xMethod = GetUltimateBaseMethod(aMethod,
|
|
xParamTypes,
|
|
aMethod.DeclaringType);
|
|
return GetMethodIdentifier(xMethod);
|
|
};
|
|
using (mTypesLocker.AcquireWriterLock())
|
|
{
|
|
xInitVmtOp.Assemble();
|
|
}
|
|
xOp = GetOpFromType(mMap.MethodFooterOp,
|
|
null,
|
|
new MethodInformation("____INIT__VMT____",
|
|
new MethodInformation.Variable[0],
|
|
new MethodInformation.Argument[0],
|
|
0,
|
|
false,
|
|
null,
|
|
null,
|
|
typeof(void),
|
|
aDebugMode,
|
|
new Dictionary<string, object>()));
|
|
xOp.Assembler = mAssembler;
|
|
xOp.Assemble();
|
|
}
|
|
|
|
private void ScanForMethodsToIncludeForVMT()
|
|
{
|
|
List<Type> xCheckedTypes = new List<Type>();
|
|
int i = -1;
|
|
while (true)
|
|
{
|
|
i++;
|
|
MethodBase xMethod;
|
|
using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
if (i == mMethods.Count)
|
|
{
|
|
break;
|
|
}
|
|
xMethod = mMethods.ElementAt(i).Key;
|
|
}
|
|
if (xMethod.IsStatic)
|
|
{
|
|
continue;
|
|
}
|
|
Type xCurrentType = xMethod.DeclaringType;
|
|
if (!xCheckedTypes.Contains(xCurrentType,
|
|
mTypesEqualityComparer))
|
|
{
|
|
xCheckedTypes.Add(xCurrentType);
|
|
}
|
|
MethodBase toBeQueuedMethod = GetUltimateBaseMethod(xMethod,
|
|
(from item in xMethod.GetParameters()
|
|
select item.ParameterType).ToArray(),
|
|
xCurrentType);
|
|
try
|
|
{
|
|
QueueMethod(toBeQueuedMethod);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new Exception("Method " + xMethod.GetFullName() + " has called " + e.Message + "! Probably it needs to be plugged");
|
|
}
|
|
}
|
|
using (mTypesLocker.AcquireReaderLock())
|
|
{
|
|
foreach (Type xType in mTypes)
|
|
{
|
|
if (!xCheckedTypes.Contains(xType,
|
|
mTypesEqualityComparer))
|
|
{
|
|
xCheckedTypes.Add(xType);
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < xCheckedTypes.Count; i++)
|
|
{
|
|
Type xCurrentType = xCheckedTypes[i];
|
|
while (xCurrentType != null)
|
|
{
|
|
if (!xCheckedTypes.Contains(xCurrentType,
|
|
mTypesEqualityComparer))
|
|
{
|
|
xCheckedTypes.Add(xCurrentType);
|
|
}
|
|
if (xCurrentType.FullName == "System.Object")
|
|
{
|
|
break;
|
|
}
|
|
if (xCurrentType.BaseType == null)
|
|
{
|
|
break;
|
|
}
|
|
xCurrentType = xCurrentType.BaseType;
|
|
}
|
|
}
|
|
foreach (Type xTD in xCheckedTypes)
|
|
{
|
|
foreach (MethodBase xMethod in xTD.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly))
|
|
{
|
|
if (!xMethod.IsStatic)
|
|
{
|
|
if (xTD.BaseType == null)
|
|
{
|
|
continue;
|
|
}
|
|
if (xMethod.IsVirtual && !xMethod.IsConstructor)
|
|
{
|
|
Type xCurrentInspectedType = xTD.BaseType;
|
|
ParameterInfo[] xParams = xMethod.GetParameters();
|
|
Type[] xMethodParams = new Type[xParams.Length];
|
|
for (int k = 0; k < xParams.Length; k++)
|
|
{
|
|
xMethodParams[k] = xParams[k].ParameterType;
|
|
}
|
|
MethodBase xBaseMethod = GetUltimateBaseMethod(xMethod,
|
|
xMethodParams,
|
|
xTD);
|
|
if (xBaseMethod != null && xBaseMethod != xMethod)
|
|
{
|
|
bool xNeedsRegistering = false;
|
|
using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
xNeedsRegistering = mMethods.ContainsKey(xBaseMethod);
|
|
}
|
|
if (xNeedsRegistering)
|
|
{
|
|
QueueMethod(xMethod);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
int j = -1;
|
|
while (true)
|
|
{
|
|
j++;
|
|
KeyValuePair<MethodBase, QueuedMethodInformation> xMethod;
|
|
using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
if (j == mMethods.Count)
|
|
{
|
|
break;
|
|
}
|
|
xMethod = mMethods.Skip(j).First();
|
|
}
|
|
if (xMethod.Key.DeclaringType.IsInterface)
|
|
{
|
|
var xInterface = xMethod.Key.DeclaringType;
|
|
i = -1;
|
|
while (true)
|
|
{
|
|
Type xImplType;
|
|
i++;
|
|
using (mTypesLocker.AcquireReaderLock())
|
|
{
|
|
if (i == mTypes.Count)
|
|
{
|
|
break;
|
|
}
|
|
xImplType = mTypes.ElementAt(i);
|
|
}
|
|
if (xImplType.IsInterface)
|
|
{
|
|
continue;
|
|
}
|
|
if (!xInterface.IsAssignableFrom(xImplType))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var xActualMethod = xImplType.GetMethod(xInterface.FullName + "." + xMethod.Key.Name,
|
|
(from xParam in xMethod.Key.GetParameters()
|
|
select xParam.ParameterType).ToArray());
|
|
|
|
if (xActualMethod == null)
|
|
{
|
|
// get private implemenation
|
|
xActualMethod = xImplType.GetMethod(xMethod.Key.Name,
|
|
(from xParam in xMethod.Key.GetParameters()
|
|
select xParam.ParameterType).ToArray());
|
|
}
|
|
if (xActualMethod == null)
|
|
{
|
|
try
|
|
{
|
|
var xMap = xImplType.GetInterfaceMap(xInterface);
|
|
for (int k = 0; k < xMap.InterfaceMethods.Length; k++)
|
|
{
|
|
if (xMap.InterfaceMethods[k] == xMethod.Key)
|
|
{
|
|
xActualMethod = xMap.TargetMethods[k];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
if (xActualMethod != null)
|
|
{
|
|
QueueMethod(xActualMethod);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static MethodBase GetUltimateBaseMethod(MethodBase aMethod,
|
|
Type[] aMethodParams,
|
|
Type aCurrentInspectedType)
|
|
{
|
|
MethodBase xBaseMethod = null;
|
|
//try {
|
|
while (true)
|
|
{
|
|
if (aCurrentInspectedType.BaseType == null)
|
|
{
|
|
break;
|
|
}
|
|
aCurrentInspectedType = aCurrentInspectedType.BaseType;
|
|
MethodBase xFoundMethod = aCurrentInspectedType.GetMethod(aMethod.Name,
|
|
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
|
|
Type.DefaultBinder,
|
|
aMethodParams,
|
|
new ParameterModifier[0]);
|
|
if (xFoundMethod == null)
|
|
{
|
|
break;
|
|
}
|
|
ParameterInfo[] xParams = xFoundMethod.GetParameters();
|
|
bool xContinue = true;
|
|
for (int i = 0; i < xParams.Length; i++)
|
|
{
|
|
if (xParams[i].ParameterType != aMethodParams[i])
|
|
{
|
|
xContinue = false;
|
|
continue;
|
|
}
|
|
}
|
|
if (!xContinue)
|
|
{
|
|
continue;
|
|
}
|
|
if (xFoundMethod != null)
|
|
{
|
|
xBaseMethod = xFoundMethod;
|
|
|
|
if (xFoundMethod.IsVirtual == aMethod.IsVirtual && xFoundMethod.IsPrivate == false && xFoundMethod.IsPublic == aMethod.IsPublic && xFoundMethod.IsFamily == aMethod.IsFamily && xFoundMethod.IsFamilyAndAssembly == aMethod.IsFamilyAndAssembly && xFoundMethod.IsFamilyOrAssembly == aMethod.IsFamilyOrAssembly && xFoundMethod.IsFinal == false)
|
|
{
|
|
var xFoundMethInfo = xFoundMethod as MethodInfo;
|
|
var xBaseMethInfo = xBaseMethod as MethodInfo;
|
|
if (xFoundMethInfo == null && xBaseMethInfo == null)
|
|
{
|
|
xBaseMethod = xFoundMethod;
|
|
}
|
|
if (xFoundMethInfo != null && xBaseMethInfo != null)
|
|
{
|
|
if (xFoundMethInfo.ReturnType.AssemblyQualifiedName.Equals(xBaseMethInfo.ReturnType.AssemblyQualifiedName))
|
|
{
|
|
xBaseMethod = xFoundMethod;
|
|
}
|
|
}
|
|
//xBaseMethod = xFoundMethod;
|
|
}
|
|
}
|
|
//else
|
|
//{
|
|
// xBaseMethod = xFoundMethod;
|
|
//}
|
|
}
|
|
//} catch (Exception) {
|
|
// todo: try to get rid of the try..catch
|
|
//}
|
|
return xBaseMethod ?? aMethod;
|
|
}
|
|
|
|
//todo: remove?
|
|
public static MethodBase GetDefinitionFromMethodBase2(MethodBase aRef)
|
|
{
|
|
Type xTypeDef;
|
|
bool xIsArray = false;
|
|
if (aRef.DeclaringType.FullName.Contains("[]") || aRef.DeclaringType.FullName.Contains("[,]") || aRef.DeclaringType.FullName.Contains("[,,]"))
|
|
{
|
|
xTypeDef = typeof(Array);
|
|
xIsArray = true;
|
|
}
|
|
else
|
|
{
|
|
xTypeDef = aRef.DeclaringType;
|
|
}
|
|
MethodBase xMethod = null;
|
|
if (xIsArray)
|
|
{
|
|
Type[] xParams = (from item in aRef.GetParameters()
|
|
select item.ParameterType).ToArray();
|
|
if (aRef.Name == "Get")
|
|
{
|
|
xMethod = xTypeDef.GetMethod("GetValue",
|
|
xParams);
|
|
}
|
|
if (aRef.Name == "Set")
|
|
{
|
|
xMethod = xTypeDef.GetMethod("SetValue",
|
|
xParams);
|
|
}
|
|
}
|
|
if (xMethod == null)
|
|
{
|
|
foreach (MethodBase xFoundMethod in xTypeDef.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
|
|
{
|
|
if (xFoundMethod.Name != aRef.Name)
|
|
{
|
|
continue;
|
|
}
|
|
string[] xRefNameParts = aRef.ToString().Split(' ');
|
|
string[] xFoundNameParts = xFoundMethod.ToString().Split(' ');
|
|
if (xFoundNameParts[0] != xRefNameParts[0])
|
|
{
|
|
//if (!(xFoundMethod.ReturnType.ReturnType is GenericParameter && aRef.ReturnType.ReturnType is GenericParameter)) {
|
|
// ArrayType xFoundArray = xFoundMethod.ReturnType.ReturnType as ArrayType;
|
|
// ArrayType xArray = aRef.ReturnType.ReturnType as ArrayType;
|
|
// if (xArray != null && xFoundArray != null) {
|
|
// if (xArray.Dimensions.Count != xFoundArray.Dimensions.Count) {
|
|
// continue;
|
|
// }
|
|
// GenericParameter xGenericParam = xArray.ElementType as GenericParameter;
|
|
// GenericParameter xFoundGenericParam = xFoundArray.ElementType as GenericParameter;
|
|
// if (xGenericParam != null && xFoundGenericParam != null) {
|
|
// if (xGenericParam.NextPosition != xFoundGenericParam.NextPosition) {
|
|
// continue;
|
|
// }
|
|
// }
|
|
// }
|
|
//}
|
|
continue;
|
|
}
|
|
ParameterInfo[] xFoundParams = xFoundMethod.GetParameters();
|
|
ParameterInfo[] xRefParams = aRef.GetParameters();
|
|
if (xFoundParams.Length != xRefParams.Length)
|
|
{
|
|
continue;
|
|
}
|
|
bool xMismatch = false;
|
|
for (int i = 0; i < xFoundParams.Length; i++)
|
|
{
|
|
if (xFoundParams[i].ParameterType.FullName != xRefParams[i].ParameterType.FullName)
|
|
{
|
|
//if (xFoundMethod.Parameters[i].ParameterType is GenericParameter && aRef.Parameters[i].ParameterType is GenericParameter) {
|
|
// continue;
|
|
//}
|
|
xMismatch = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!xMismatch)
|
|
{
|
|
xMethod = xFoundMethod;
|
|
}
|
|
}
|
|
}
|
|
if (xMethod != null)
|
|
{
|
|
return xMethod;
|
|
}
|
|
//xMethod = xTypeDef.GetConstructor(aRef.Name == MethodBase.Cctor, aRef.Parameters);
|
|
//if (xMethod != null && (aRef.Name == MethodBase.Cctor || aRef.Name == MethodBase.Ctor)) {
|
|
// return xMethod;
|
|
//}
|
|
throw new Exception("Couldn't find Method! ('" + aRef.GetFullName() + "'");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gives the size to store an instance of the <paramref name="aType"/> for use in a field.
|
|
/// </summary>
|
|
/// <remarks>For classes, this is the pointer size.</remarks>
|
|
/// <param name="aType"></param>
|
|
/// <returns></returns>
|
|
public static uint GetFieldStorageSize(Type aType)
|
|
{
|
|
if (aType.FullName == "System.Void")
|
|
{
|
|
return 0;
|
|
}
|
|
if ((!aType.IsValueType && aType.IsClass) || aType.IsInterface)
|
|
{
|
|
return 4;
|
|
}
|
|
switch (aType.FullName)
|
|
{
|
|
case "System.Char":
|
|
return 2;
|
|
case "System.Byte":
|
|
case "System.SByte":
|
|
return 1;
|
|
case "System.UInt16":
|
|
case "System.Int16":
|
|
return 2;
|
|
case "System.UInt32":
|
|
case "System.Int32":
|
|
return 4;
|
|
case "System.UInt64":
|
|
case "System.Int64":
|
|
return 8;
|
|
// for now hardcode IntPtr and UIntPtr to be 32-bit
|
|
case "System.UIntPtr":
|
|
case "System.IntPtr":
|
|
return 4;
|
|
case "System.Boolean":
|
|
return 1;
|
|
case "System.Single":
|
|
return 4;
|
|
case "System.Double":
|
|
return 8;
|
|
case "System.Decimal":
|
|
return 16;
|
|
case "System.Guid":
|
|
return 16;
|
|
case "System.DateTime":
|
|
return 8; // todo: check for correct size
|
|
}
|
|
if (aType.FullName.EndsWith("*"))
|
|
{
|
|
// pointer
|
|
return 4;
|
|
}
|
|
// array
|
|
//TypeSpecification xTypeSpec = aType as TypeSpecification;
|
|
//if (xTypeSpec != null) {
|
|
// return 4;
|
|
//}
|
|
if (aType.IsEnum)
|
|
{
|
|
return GetFieldStorageSize(aType.GetField("value__").FieldType);
|
|
}
|
|
if (aType.IsValueType)
|
|
{
|
|
StructLayoutAttribute xSLA = aType.StructLayoutAttribute;
|
|
if (xSLA != null)
|
|
{
|
|
if (xSLA.Size > 0)
|
|
{
|
|
return (uint)xSLA.Size;
|
|
}
|
|
}
|
|
}
|
|
uint xResult;
|
|
GetTypeFieldInfo(aType,
|
|
out xResult);
|
|
return xResult;
|
|
}
|
|
|
|
private static string GetGroupForType(Type aType)
|
|
{
|
|
return aType.Module.Assembly.GetName().Name;
|
|
}
|
|
|
|
protected void EmitTracer(Op aOp, string aNamespace, int aPos, int[] aCodeOffsets, string aLabel)
|
|
{
|
|
// NOTE - These if statemens can be optimized down - but clarity is
|
|
// more importnat the optimizations would not offer much benefit
|
|
|
|
// Determine if a new DebugStub should be emitted
|
|
//bool xEmit = false;
|
|
// Skip NOOP's so we dont have breakpoints on them
|
|
//TODO: Each IL op should exist in IL, and descendants in IL.X86.
|
|
// Because of this we have this hack
|
|
if (aOp.ToString() == "Indy.IL2CPU.IL.X86.Nop")
|
|
{
|
|
return;
|
|
}
|
|
else if (mDebugMode == DebugMode.None)
|
|
{
|
|
return;
|
|
}
|
|
else if (mDebugMode == DebugMode.Source)
|
|
{
|
|
// If the current position equals one of the offsets, then we have
|
|
// reached a new atomic C# statement
|
|
if (aCodeOffsets != null)
|
|
{
|
|
if (aCodeOffsets.Contains(aPos) == false)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check options for Debug Level
|
|
// Set based on TracedAssemblies
|
|
if (TraceAssemblies == TraceAssemblies.Cosmos || TraceAssemblies == TraceAssemblies.User)
|
|
{
|
|
if (aNamespace.StartsWith("System.", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
return;
|
|
}
|
|
else if (aNamespace.ToLower() == "system")
|
|
{
|
|
return;
|
|
}
|
|
else if (aNamespace.StartsWith("Microsoft.", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if (TraceAssemblies == TraceAssemblies.User)
|
|
{
|
|
//TODO: Maybe an attribute that could be used to turn tracing on and off
|
|
//TODO: This doesnt match Cosmos.Kernel exact vs Cosmos.Kernel., so a user
|
|
// could do Cosmos.KernelMine and it will fail. Need to fix this
|
|
if (aNamespace.StartsWith("Cosmos.Kernel", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
return;
|
|
}
|
|
else if (aNamespace.StartsWith("Cosmos.Sys", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
return;
|
|
}
|
|
else if (aNamespace.StartsWith("Cosmos.Hardware", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
return;
|
|
}
|
|
else if (aNamespace.StartsWith("Indy.IL2CPU", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
// If we made it this far, emit the Tracer
|
|
mMap.EmitOpDebugHeader(mAssembler, 0, aLabel);
|
|
}
|
|
|
|
private void ProcessAllStaticFields()
|
|
{
|
|
int i = -1;
|
|
int xCount = 0;
|
|
while (true)
|
|
{
|
|
i++;
|
|
FieldInfo xCurrentField;
|
|
using (mStaticFieldsLocker.AcquireReaderLock())
|
|
{
|
|
xCount = mStaticFields.Count;
|
|
if (i == xCount)
|
|
{
|
|
break;
|
|
}
|
|
xCurrentField = mStaticFields.Keys.ElementAt(i);
|
|
}
|
|
CompilingStaticFields(i, xCount);
|
|
//ProgressChanged.Invoke(String.Format("Processing static field: {0}", xCurrentField.GetFullName()));
|
|
string xFieldName = xCurrentField.GetFullName();
|
|
xFieldName = DataMember.GetStaticFieldName(xCurrentField);
|
|
if (mAssembler.DataMembers.Count(x => x.Name == xFieldName) == 0)
|
|
{
|
|
var xItem = (from item in xCurrentField.GetCustomAttributes(false)
|
|
where item.GetType().FullName == "ManifestResourceStreamAttribute"
|
|
select item).FirstOrDefault();
|
|
string xManifestResourceName = null;
|
|
if (xItem != null)
|
|
{
|
|
var xItemType = xItem.GetType();
|
|
xManifestResourceName = (string)xItemType.GetField("ResourceName").GetValue(xItem);
|
|
}
|
|
if (xManifestResourceName != null)
|
|
{
|
|
//RegisterType(xCurrentField.FieldType);
|
|
//string xFileName = Path.Combine(mOutputDir,
|
|
// (xCurrentField.DeclaringType.Assembly.FullName + "__" + xManifestResourceName).Replace(",",
|
|
// "_") + ".res");
|
|
//using (var xStream = xCurrentField.DeclaringType.Assembly.GetManifestResourceStream(xManifestResourceName)) {
|
|
// if (xStream == null) {
|
|
// throw new Exception("Resource '" + xManifestResourceName + "' not found!");
|
|
// }
|
|
// using (var xTarget = File.Create(xFileName)) {
|
|
// // todo: abstract this array code out.
|
|
// xTarget.Write(BitConverter.GetBytes(Engine.RegisterType(Engine.GetType("mscorlib",
|
|
// "System.Array"))),
|
|
// 0,
|
|
// 4);
|
|
// xTarget.Write(BitConverter.GetBytes((uint)InstanceTypeEnum.StaticEmbeddedArray),
|
|
// 0,
|
|
// 4);
|
|
// xTarget.Write(BitConverter.GetBytes((int)xStream.Length), 0, 4);
|
|
// xTarget.Write(BitConverter.GetBytes((int)1), 0, 4);
|
|
// var xBuff = new byte[128];
|
|
// while (xStream.Position < xStream.Length) {
|
|
// int xBytesRead = xStream.Read(xBuff, 0, 128);
|
|
// xTarget.Write(xBuff, 0, xBytesRead);
|
|
// }
|
|
// }
|
|
//}
|
|
//mAssembler.DataMembers.Add(new DataMember("___" + xFieldName + "___Contents",
|
|
// "incbin",
|
|
// "\"" + xFileName + "\""));
|
|
//mAssembler.DataMembers.Add(new DataMember(xFieldName,
|
|
// "dd",
|
|
// "___" + xFieldName + "___Contents"));
|
|
throw new NotImplementedException();
|
|
}
|
|
else
|
|
{
|
|
RegisterType(xCurrentField.FieldType);
|
|
uint xTheSize;
|
|
//string theType = "db";
|
|
Type xFieldTypeDef = xCurrentField.FieldType;
|
|
if (!xFieldTypeDef.IsClass || xFieldTypeDef.IsValueType)
|
|
{
|
|
xTheSize = GetFieldStorageSize(xCurrentField.FieldType);
|
|
}
|
|
else
|
|
{
|
|
xTheSize = 4;
|
|
}
|
|
byte[] xData = new byte[xTheSize];
|
|
try
|
|
{
|
|
object xValue = xCurrentField.GetValue(null);
|
|
if (xValue != null)
|
|
{
|
|
try
|
|
{
|
|
xData = new byte[xTheSize];
|
|
if (xValue.GetType().IsValueType)
|
|
{
|
|
for (int x = 0; x < xTheSize; x++)
|
|
{
|
|
xData[x] = Marshal.ReadByte(xValue,
|
|
x);
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
mAssembler.DataMembers.Add(new DataMember(xFieldName, xData));
|
|
}
|
|
}
|
|
using (mStaticFieldsLocker.AcquireReaderLock())
|
|
{
|
|
mStaticFields[xCurrentField].Processed = true;
|
|
}
|
|
}
|
|
CompilingStaticFields(i, xCount);
|
|
}
|
|
|
|
private ISymbolReader GetSymbolReaderForAssembly(Assembly aAssembly)
|
|
{
|
|
try
|
|
{
|
|
return SymbolAccess.GetReaderForFile(aAssembly.Location);
|
|
}
|
|
catch (NotSupportedException)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private void ProcessAllMethods()
|
|
{
|
|
int i = -1;
|
|
int xCount = 0;
|
|
|
|
int EMITi = 0;
|
|
List<MethodBase> EMITdefs = null;
|
|
bool EMITmode = false;
|
|
|
|
while (true)
|
|
{
|
|
if (EMITmode)
|
|
{
|
|
EMITi++;
|
|
}
|
|
else
|
|
{
|
|
i++;
|
|
}
|
|
MethodBase xCurrentMethod;
|
|
using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
xCount = mMethods.Count;
|
|
EMITmode = (i == xCount);
|
|
if (EMITmode)
|
|
{
|
|
if (EMITdefs == null)
|
|
{
|
|
EMITdefs = new List<MethodBase>();
|
|
Assembly EMITassm = (from assm in AppDomain.CurrentDomain.GetAssemblies() where assm.GetName().Name == "IndyIL2CPU_EmitAssm" select assm).FirstOrDefault<Assembly>();
|
|
foreach (Type EMITtype in EMITassm.GetTypes())
|
|
{
|
|
EMITdefs.AddRange(from method in EMITtype.GetMethods() where method.Name.StartsWith("Emit") select method as MethodBase);
|
|
}
|
|
}
|
|
|
|
if (EMITi == EMITdefs.Count)
|
|
break;
|
|
|
|
xCurrentMethod = EMITdefs[EMITi];
|
|
CompilingMethods(EMITi, EMITdefs.Count);
|
|
}
|
|
else
|
|
{
|
|
xCurrentMethod = mMethods.Keys.ElementAt(i);
|
|
if (DynamicMethodEmit.GetHasDynamicMethod(xCurrentMethod))
|
|
continue;
|
|
CompilingMethods(i, xCount);
|
|
}
|
|
}
|
|
|
|
OnDebugLog(LogSeverityEnum.Informational, "Processing method {0}", xCurrentMethod.GetFullName());
|
|
try
|
|
{
|
|
EmitDependencyGraphLine(true, xCurrentMethod.GetFullName());
|
|
RegisterType(xCurrentMethod.DeclaringType);
|
|
if (xCurrentMethod.IsAbstract)
|
|
{
|
|
using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
mMethods[xCurrentMethod].Processed = true;
|
|
}
|
|
continue;
|
|
}
|
|
string xMethodName = Label.GenerateLabelName(xCurrentMethod);
|
|
TypeInformation xTypeInfo = null;
|
|
if (!xCurrentMethod.IsStatic)
|
|
{
|
|
xTypeInfo = GetTypeInfo(xCurrentMethod.DeclaringType);
|
|
}
|
|
SortedList<string, object> xMethodScanInfo;
|
|
using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
if (EMITmode)
|
|
{
|
|
xMethodScanInfo = new QueuedMethodInformation().Info;
|
|
}
|
|
else
|
|
{
|
|
xMethodScanInfo = mMethods[xCurrentMethod].Info;
|
|
}
|
|
}
|
|
MethodInformation xMethodInfo = GetMethodInfo(xCurrentMethod, xCurrentMethod
|
|
, xMethodName, xTypeInfo, mDebugMode != DebugMode.None, xMethodScanInfo);
|
|
|
|
Op xOp = GetOpFromType(mMap.MethodHeaderOp, null, xMethodInfo);
|
|
xOp.Assembler = mAssembler;
|
|
#if VERBOSE_DEBUG
|
|
string comment = "(No Type Info available)";
|
|
if (xMethodInfo.TypeInfo != null)
|
|
{
|
|
comment = "Type Info:\r\n \r\n" + xMethodInfo.TypeInfo;
|
|
}
|
|
foreach (string s in comment.Trim().Split(new string[] { "\r\n" }
|
|
, StringSplitOptions.RemoveEmptyEntries))
|
|
{
|
|
new Comment(s);
|
|
}
|
|
comment = xMethodInfo.ToString();
|
|
foreach (string s in comment.Trim().Split(new string[] { "\r\n" }
|
|
, StringSplitOptions.RemoveEmptyEntries))
|
|
{
|
|
new Comment(s);
|
|
}
|
|
#endif
|
|
xOp.Assemble();
|
|
MethodBase xCustomImplementation = GetCustomMethodImplementation(xMethodName);
|
|
bool xIsCustomImplementation = (xCustomImplementation != null);
|
|
// what to do if a method doesn't have a body?
|
|
bool xContentProduced = false;
|
|
if (xIsCustomImplementation)
|
|
{
|
|
// this is for the support for having extra fields on types, and being able to use
|
|
// them in custom implementation methods
|
|
CustomMethodImplementationProxyOp xProxyOp
|
|
= (CustomMethodImplementationProxyOp)GetOpFromType(
|
|
mMap.CustomMethodImplementationProxyOp, null, xMethodInfo);
|
|
xProxyOp.Assembler = mAssembler;
|
|
xProxyOp.ProxiedMethod = xCustomImplementation;
|
|
xProxyOp.Assemble();
|
|
xContentProduced = true;
|
|
}
|
|
if (!xContentProduced)
|
|
{
|
|
Type xOpType = mMap.GetOpForCustomMethodImplementation(xMethodName);
|
|
if (xOpType != null)
|
|
{
|
|
Op xMethodOp = GetOpFromType(xOpType, null, xMethodInfo);
|
|
if (xMethodOp != null)
|
|
{
|
|
xMethodOp.Assembler = mAssembler;
|
|
xMethodOp.Assemble();
|
|
xContentProduced = true;
|
|
}
|
|
}
|
|
}
|
|
if (!xContentProduced)
|
|
{
|
|
if (mMap.HasCustomAssembleImplementation(xMethodInfo))
|
|
{
|
|
mMap.DoCustomAssembleImplementation(mAssembler, xMethodInfo);
|
|
// No plugs, we need to compile the IL from the method
|
|
}
|
|
else
|
|
{
|
|
MethodBody xBody = xCurrentMethod.GetMethodBody();
|
|
// todo: add better detection of implementation state
|
|
if (xBody != null)
|
|
{
|
|
mInstructionsToSkip = 0;
|
|
mAssembler.StackContents.Clear();
|
|
var xReader = new ILReader(xCurrentMethod);
|
|
var xInstructionInfos = new List<DebugSymbolsAssemblyTypeMethodInstruction>();
|
|
|
|
// Section currently is dead code. Working on matching it up
|
|
// with contents from inside the read
|
|
int[] xCodeOffsets = null;
|
|
if (mDebugMode == DebugMode.Source)
|
|
{
|
|
var xSymbolReader = GetSymbolReaderForAssembly(xCurrentMethod.DeclaringType.Assembly);
|
|
if (xSymbolReader != null)
|
|
{
|
|
var xSmbMethod = xSymbolReader.GetMethod(new SymbolToken(xCurrentMethod.MetadataToken));
|
|
// This gets the Sequence Points.
|
|
// Sequence Points are spots that identify what the compiler/debugger says is a spot
|
|
// that a breakpoint can occur one. Essentially, an atomic source line in C#
|
|
if (xSmbMethod != null)
|
|
{
|
|
xCodeOffsets = new int[xSmbMethod.SequencePointCount];
|
|
var xCodeDocuments = new ISymbolDocument[xSmbMethod.SequencePointCount];
|
|
var xCodeLines = new int[xSmbMethod.SequencePointCount];
|
|
var xCodeColumns = new int[xSmbMethod.SequencePointCount];
|
|
var xCodeEndLines = new int[xSmbMethod.SequencePointCount];
|
|
var xCodeEndColumns = new int[xSmbMethod.SequencePointCount];
|
|
xSmbMethod.GetSequencePoints(xCodeOffsets, xCodeDocuments
|
|
, xCodeLines, xCodeColumns, xCodeEndLines, xCodeEndColumns);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Scan each IL op in the method
|
|
while (xReader.Read())
|
|
{
|
|
ExceptionHandlingClause xCurrentHandler = null;
|
|
|
|
#region Exception handling support code
|
|
// todo: add support for nested handlers using a stack or so..
|
|
foreach (ExceptionHandlingClause xHandler in xBody.ExceptionHandlingClauses)
|
|
{
|
|
if (xHandler.TryOffset > 0)
|
|
{
|
|
if (xHandler.TryOffset <= xReader.NextPosition && (xHandler.TryLength + xHandler.TryOffset) > xReader.NextPosition)
|
|
{
|
|
if (xCurrentHandler == null)
|
|
{
|
|
xCurrentHandler = xHandler;
|
|
continue;
|
|
}
|
|
else if (xHandler.TryOffset > xCurrentHandler.TryOffset && (xHandler.TryLength + xHandler.TryOffset) < (xCurrentHandler.TryLength + xCurrentHandler.TryOffset))
|
|
{
|
|
// only replace if the current found handler is narrower
|
|
xCurrentHandler = xHandler;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if (xHandler.HandlerOffset > 0)
|
|
{
|
|
if (xHandler.HandlerOffset <= xReader.NextPosition && (xHandler.HandlerOffset + xHandler.HandlerLength) > xReader.NextPosition)
|
|
{
|
|
if (xCurrentHandler == null)
|
|
{
|
|
xCurrentHandler = xHandler;
|
|
continue;
|
|
}
|
|
else if (xHandler.HandlerOffset > xCurrentHandler.HandlerOffset && (xHandler.HandlerOffset + xHandler.HandlerLength) < (xCurrentHandler.HandlerOffset + xCurrentHandler.HandlerLength))
|
|
{
|
|
// only replace if the current found handler is narrower
|
|
xCurrentHandler = xHandler;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if ((xHandler.Flags & ExceptionHandlingClauseOptions.Filter) > 0)
|
|
{
|
|
if (xHandler.FilterOffset > 0)
|
|
{
|
|
if (xHandler.FilterOffset <= xReader.NextPosition)
|
|
{
|
|
if (xCurrentHandler == null)
|
|
{
|
|
xCurrentHandler = xHandler;
|
|
continue;
|
|
}
|
|
else if (xHandler.FilterOffset > xCurrentHandler.FilterOffset)
|
|
{
|
|
// only replace if the current found handler is narrower
|
|
xCurrentHandler = xHandler;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
xMethodInfo.CurrentHandler = xCurrentHandler;
|
|
xOp = GetOpFromType(mMap.GetOpForOpCode(xReader.OpCode), xReader, xMethodInfo);
|
|
|
|
xOp.Assembler = mAssembler;
|
|
new Comment("StackItems = " + mAssembler.StackContents.Count);
|
|
foreach (var xStackContent in mAssembler.StackContents)
|
|
{
|
|
new Comment(" " + xStackContent.Size);
|
|
}
|
|
|
|
// Create label for current point
|
|
string xLabel = Op.GetInstructionLabel(xReader);
|
|
if (xLabel.StartsWith("."))
|
|
{
|
|
xLabel = DataMember.FilterStringForIncorrectChars(
|
|
Label.LastFullLabel + "__DOT__" + xLabel.Substring(1));
|
|
}
|
|
|
|
// Possibly emit Tracer call
|
|
EmitTracer(xOp, xCurrentMethod.DeclaringType.Namespace, (int)xReader.Position,
|
|
xCodeOffsets, xLabel);
|
|
|
|
using (mSymbolsLocker.AcquireWriterLock())
|
|
{
|
|
if (mSymbols != null)
|
|
{
|
|
var xMLSymbol = new MLDebugSymbol();
|
|
xMLSymbol.LabelName = xLabel;
|
|
int xStackSize = (from item in mAssembler.StackContents
|
|
let xSize = (item.Size % 4 == 0)
|
|
? item.Size
|
|
: (item.Size + (4 - (item.Size % 4)))
|
|
select xSize).Sum();
|
|
xMLSymbol.StackDifference = xMethodInfo.LocalsSize + xStackSize;
|
|
try
|
|
{
|
|
xMLSymbol.AssemblyFile = xCurrentMethod.DeclaringType.Assembly.Location;
|
|
}
|
|
catch (NotSupportedException)
|
|
{
|
|
xMLSymbol.AssemblyFile = "DYNAMIC: " + xCurrentMethod.DeclaringType.Assembly.FullName;
|
|
}
|
|
xMLSymbol.MethodToken = xCurrentMethod.MetadataToken;
|
|
xMLSymbol.TypeToken = xCurrentMethod.DeclaringType.MetadataToken;
|
|
xMLSymbol.ILOffset = (int)xReader.Position;
|
|
mSymbols.Add(xMLSymbol);
|
|
}
|
|
}
|
|
xOp.Assemble();
|
|
//if (xInstructionInfo != null) {
|
|
// int xNewStack = (from item in mAssembler.StackContents
|
|
// let xSize = (item.Size % 4 == 0) ? item.Size : (item.Size + (4 - (item.Size % 4)))
|
|
// select xSize).Sum();
|
|
// xInstructionInfo.StackResult = xNewStack - xCurrentStack;
|
|
// xInstructionInfo.StackResultSpecified = true;
|
|
// xInstructionInfos.Add(xInstructionInfo);
|
|
//}
|
|
}
|
|
if (mSymbols != null && !EMITmode)
|
|
{
|
|
MLDebugSymbol[] xSymbols;
|
|
using (mSymbolsLocker.AcquireReaderLock())
|
|
{
|
|
xSymbols = mSymbols.ToArray();
|
|
}
|
|
using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
mMethods[xCurrentMethod].Instructions = xSymbols;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((xCurrentMethod.Attributes & MethodAttributes.PinvokeImpl) != 0)
|
|
{
|
|
OnDebugLog(LogSeverityEnum.Error,
|
|
"Method '{0}' not generated!",
|
|
xCurrentMethod.GetFullName());
|
|
new Comment("Method not being generated yet, as it's handled by a PInvoke");
|
|
}
|
|
else
|
|
{
|
|
OnDebugLog(LogSeverityEnum.Error,
|
|
"Method '{0}' not generated!",
|
|
xCurrentMethod.GetFullName());
|
|
new Comment("Method not being generated yet, as it's handled by an iCall");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
xOp = GetOpFromType(mMap.MethodFooterOp, null, xMethodInfo);
|
|
xOp.Assembler = mAssembler;
|
|
xOp.Assemble();
|
|
mAssembler.StackContents.Clear();
|
|
if (!EMITmode)
|
|
{
|
|
using (mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
mMethods[xCurrentMethod].Processed = true;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
OnDebugLog(LogSeverityEnum.Error, xCurrentMethod.GetFullName());
|
|
OnDebugLog(LogSeverityEnum.Warning, e.ToString());
|
|
throw;
|
|
}
|
|
}
|
|
|
|
//BUG //HACK
|
|
//if (EMITmode)
|
|
//{
|
|
// CompilingMethods(EMITi, EMITdefs.Count); //cant see the point of sending 0,0 prob bug
|
|
//}
|
|
//else
|
|
{
|
|
CompilingMethods(i, xCount);
|
|
}
|
|
}
|
|
|
|
private IList<Assembly> GetPlugAssemblies()
|
|
{
|
|
var xResult = this.mMap.GetPlugAssemblies();
|
|
xResult.Add(typeof(Engine).Assembly);
|
|
return xResult;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the full name of a method, without the defining type included
|
|
/// </summary>
|
|
/// <param name="aMethod"></param>
|
|
/// <returns></returns>
|
|
private static string GetStrippedMethodBaseFullName(MethodBase aMethod,
|
|
MethodBase aRefMethod)
|
|
{
|
|
StringBuilder xBuilder = new StringBuilder();
|
|
string[] xParts = aMethod.ToString().Split(' ');
|
|
string[] xParts2 = xParts.Skip(1).ToArray();
|
|
MethodInfo xMethodInfo = aMethod as MethodInfo;
|
|
if (xMethodInfo != null)
|
|
{
|
|
xBuilder.Append(xMethodInfo.ReturnType.FullName);
|
|
}
|
|
else
|
|
{
|
|
if (aMethod is ConstructorInfo)
|
|
{
|
|
xBuilder.Append(typeof(void).FullName);
|
|
}
|
|
else
|
|
{
|
|
xBuilder.Append(xParts[0]);
|
|
}
|
|
}
|
|
xBuilder.Append(" ");
|
|
xBuilder.Append(".");
|
|
xBuilder.Append(aMethod.Name);
|
|
xBuilder.Append("(");
|
|
ParameterInfo[] xParams = aMethod.GetParameters();
|
|
bool xParamAdded = false;
|
|
for (int i = 0; i < xParams.Length; i++)
|
|
{
|
|
if (i == 0 && (aRefMethod != null && !aRefMethod.IsStatic))
|
|
{
|
|
continue;
|
|
}
|
|
if (xParams[i].IsDefined(typeof(FieldAccessAttribute), true))
|
|
{
|
|
continue;
|
|
}
|
|
if (xParamAdded)
|
|
{
|
|
xBuilder.Append(", ");
|
|
}
|
|
xBuilder.Append(xParams[i].ParameterType.FullName);
|
|
xParamAdded = true;
|
|
}
|
|
xBuilder.Append(")");
|
|
return xBuilder.ToString();
|
|
}
|
|
|
|
private void InitializePlugs(IEnumerable<string> aPlugs)
|
|
{
|
|
if (mPlugMethods != null)
|
|
{
|
|
throw new Exception("PlugMethods list already initialized!");
|
|
}
|
|
if (mPlugFields != null)
|
|
{
|
|
throw new Exception("PlugFields list already initialized!");
|
|
}
|
|
|
|
mPlugMethods = new SortedList<string, MethodBase>();
|
|
mPlugFields = new SortedList<Type, Dictionary<string, PlugFieldAttribute>>(new TypeComparer());
|
|
|
|
AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad;
|
|
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
|
|
foreach (var xAsm in AppDomain.CurrentDomain.GetAssemblies())
|
|
{
|
|
CheckAssemblyForPlugAssemblies(xAsm);
|
|
}
|
|
List<Assembly> xPlugs = new List<Assembly>();
|
|
var xComparer = new AssemblyEqualityComparer();
|
|
|
|
foreach (string s in aPlugs)
|
|
{
|
|
Assembly a = Assembly.LoadFrom(s);
|
|
a.GetTypes();
|
|
if (!xPlugs.Contains(a,
|
|
xComparer))
|
|
{
|
|
xPlugs.Add(a);
|
|
}
|
|
}
|
|
|
|
foreach (var item in GetPlugAssemblies())
|
|
{
|
|
if (!xPlugs.Contains(item,
|
|
xComparer))
|
|
{
|
|
xPlugs.Add(item);
|
|
}
|
|
}
|
|
|
|
foreach (Assembly xAssemblyDef in xPlugs)
|
|
{
|
|
LoadPlugAssembly(xAssemblyDef);
|
|
}
|
|
}
|
|
|
|
private Assembly CurrentDomain_AssemblyResolve(object sender,
|
|
ResolveEventArgs args)
|
|
{
|
|
if (File.Exists(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
|
|
args.Name + ".dll")))
|
|
{
|
|
return Assembly.ReflectionOnlyLoadFrom(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
|
|
args.Name + ".dll"));
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void CurrentDomain_AssemblyLoad(object sender,
|
|
AssemblyLoadEventArgs args)
|
|
{
|
|
CheckAssemblyForPlugAssemblies(args.LoadedAssembly);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load any plug assemblies referred to in this assembly's .config file.
|
|
/// </summary>
|
|
private void CheckAssemblyForPlugAssemblies(Assembly aAssembly)
|
|
{
|
|
//If in the GAC, then ignore assembly
|
|
if (aAssembly.GlobalAssemblyCache)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Search for related .config file
|
|
string configFile = aAssembly.Location + ".cosmos-config";
|
|
if (System.IO.File.Exists(configFile))
|
|
{
|
|
//Load and parse all PlugAssemblies referred to in the .config file
|
|
foreach (Assembly xAssembly in GetAssembliesFromConfigFile(configFile))
|
|
{
|
|
LoadPlugAssembly(xAssembly);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves a list of plug assemblies from the given .config file.
|
|
/// </summary>
|
|
/// <param name="configFile"></param>
|
|
private IEnumerable<Assembly> GetAssembliesFromConfigFile(string configFile)
|
|
{
|
|
//Parse XML and get all the PlugAssembly names
|
|
XmlDocument xml = new XmlDocument();
|
|
xml.Load(configFile);
|
|
// do version check:
|
|
if (xml.DocumentElement.Attributes["version"] == null || xml.DocumentElement.Attributes["version"].Value != "1")
|
|
{
|
|
throw new Exception(".DLL configuration version mismatch!");
|
|
}
|
|
|
|
string xHintPath = null;
|
|
if (xml.DocumentElement.Attributes["hintpath"] != null)
|
|
{
|
|
xHintPath = xml.DocumentElement.Attributes["hintpath"].Value;
|
|
}
|
|
foreach (XmlNode assemblyName in xml.GetElementsByTagName("plug-assembly"))
|
|
{
|
|
string xName = assemblyName.InnerText;
|
|
if (xName.EndsWith(".dll",
|
|
StringComparison.InvariantCultureIgnoreCase) || xName.EndsWith(".exe",
|
|
StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
if (!String.IsNullOrEmpty(xHintPath))
|
|
{
|
|
yield return Assembly.LoadFile(Path.Combine(xHintPath,
|
|
xName));
|
|
continue;
|
|
}
|
|
}
|
|
yield return Assembly.Load(assemblyName.InnerText);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Searches assembly for methods or fields marked with custom attributes PlugMethodAttribute or PlugFieldAttribute.
|
|
/// Matches found are inserted in SortedLists mPlugMethods and mPlugFields.
|
|
/// </summary>
|
|
private void LoadPlugAssembly(Assembly aAssemblyDef)
|
|
{
|
|
foreach (var xType in (from item in aAssemblyDef.GetTypes()
|
|
let xCustomAttribs = item.GetCustomAttributes(typeof(PlugAttribute),
|
|
false)
|
|
where xCustomAttribs != null && xCustomAttribs.Length > 0
|
|
select new KeyValuePair<Type, PlugAttribute>(item,
|
|
(PlugAttribute)xCustomAttribs[0])))
|
|
{
|
|
PlugAttribute xPlugAttrib = xType.Value;
|
|
if (xPlugAttrib.IsMonoOnly && !RunningOnMono)
|
|
{
|
|
continue;
|
|
}
|
|
if (xPlugAttrib.IsMicrosoftdotNETOnly && RunningOnMono)
|
|
{
|
|
continue;
|
|
}
|
|
Type xTypeRef = xPlugAttrib.Target;
|
|
if (xTypeRef == null)
|
|
{
|
|
xTypeRef = Type.GetType(xPlugAttrib.TargetName,
|
|
true);
|
|
}
|
|
|
|
PlugFieldAttribute[] xTypePlugFields = xType.Key.GetCustomAttributes(typeof(PlugFieldAttribute),
|
|
false) as PlugFieldAttribute[];
|
|
if (xTypePlugFields != null && xTypePlugFields.Length > 0)
|
|
{
|
|
Dictionary<string, PlugFieldAttribute> xPlugFields;
|
|
if (mPlugFields.ContainsKey(xTypeRef))
|
|
{
|
|
xPlugFields = mPlugFields[xTypeRef];
|
|
}
|
|
else
|
|
{
|
|
mPlugFields.Add(xTypeRef,
|
|
xPlugFields = new Dictionary<string, PlugFieldAttribute>());
|
|
}
|
|
foreach (var xPlugField in xTypePlugFields)
|
|
{
|
|
if (xPlugAttrib.IsMonoOnly && !RunningOnMono)
|
|
{
|
|
continue;
|
|
}
|
|
if (xPlugAttrib.IsMicrosoftdotNETOnly && RunningOnMono)
|
|
{
|
|
continue;
|
|
}
|
|
if (!xPlugFields.ContainsKey(xPlugField.FieldId))
|
|
{
|
|
xPlugFields.Add(xPlugField.FieldId,
|
|
xPlugField);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (MethodBase xMethod in xType.Key.GetMethods(BindingFlags.Public | BindingFlags.Static))
|
|
{
|
|
PlugMethodAttribute xPlugMethodAttrib = xMethod.GetCustomAttributes(typeof(PlugMethodAttribute),
|
|
true).Cast<PlugMethodAttribute>().FirstOrDefault();
|
|
string xSignature = String.Empty;
|
|
if (xPlugMethodAttrib != null)
|
|
{
|
|
xSignature = xPlugMethodAttrib.Signature;
|
|
if (!xPlugMethodAttrib.Enabled)
|
|
{
|
|
continue;
|
|
}
|
|
if (xPlugAttrib.IsMonoOnly && !RunningOnMono)
|
|
{
|
|
continue;
|
|
}
|
|
if (xPlugAttrib.IsMicrosoftdotNETOnly && RunningOnMono)
|
|
{
|
|
continue;
|
|
}
|
|
if (!String.IsNullOrEmpty(xSignature))
|
|
{
|
|
if (!mPlugMethods.ContainsKey(xSignature))
|
|
{
|
|
mPlugMethods.Add(xSignature,
|
|
xMethod);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
foreach (MethodBase xOrigMethodDef in xTypeRef.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic))
|
|
{
|
|
string xStrippedSignature = GetStrippedMethodBaseFullName(xMethod,
|
|
xOrigMethodDef);
|
|
string xOrigStrippedSignature = GetStrippedMethodBaseFullName(xOrigMethodDef,
|
|
null);
|
|
if (xOrigStrippedSignature == xStrippedSignature)
|
|
{
|
|
if (!mPlugMethods.ContainsKey(Label.GenerateLabelName(xOrigMethodDef)))
|
|
{
|
|
mPlugMethods.Add(Label.GenerateLabelName(xOrigMethodDef),
|
|
xMethod);
|
|
}
|
|
}
|
|
}
|
|
foreach (MethodBase xOrigMethodDef in xTypeRef.GetConstructors(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic))
|
|
{
|
|
string xStrippedSignature = GetStrippedMethodBaseFullName(xMethod,
|
|
xOrigMethodDef);
|
|
string xOrigStrippedSignature = GetStrippedMethodBaseFullName(xOrigMethodDef,
|
|
null);
|
|
if (xOrigStrippedSignature == xStrippedSignature)
|
|
{
|
|
if (mPlugMethods.ContainsKey(Label.GenerateLabelName(xOrigMethodDef)))
|
|
{
|
|
System.Diagnostics.Debugger.Break();
|
|
}
|
|
mPlugMethods.Add(Label.GenerateLabelName(xOrigMethodDef),
|
|
xMethod);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private MethodBase GetCustomMethodImplementation(string aMethodName)
|
|
{
|
|
if (mPlugMethods.ContainsKey(aMethodName))
|
|
{
|
|
return mPlugMethods[aMethodName];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static TypeInformation GetTypeInfo(Type aType)
|
|
{
|
|
TypeInformation xTypeInfo;
|
|
uint xObjectStorageSize;
|
|
Dictionary<string, TypeInformation.Field> xTypeFields = GetTypeFieldInfo(aType,
|
|
out xObjectStorageSize);
|
|
xTypeInfo = new TypeInformation(xObjectStorageSize,
|
|
xTypeFields,
|
|
aType,
|
|
(!aType.IsValueType) && aType.IsClass);
|
|
return xTypeInfo;
|
|
}
|
|
|
|
public static MethodInformation GetMethodInfo(MethodBase aCurrentMethodForArguments,
|
|
MethodBase aCurrentMethodForLocals,
|
|
string aMethodName,
|
|
TypeInformation aTypeInfo,
|
|
bool aDebugMode)
|
|
{
|
|
return GetMethodInfo(aCurrentMethodForArguments,
|
|
aCurrentMethodForLocals,
|
|
aMethodName,
|
|
aTypeInfo,
|
|
aDebugMode,
|
|
null);
|
|
}
|
|
|
|
public static MethodInformation GetMethodInfo(MethodBase aCurrentMethodForArguments,
|
|
MethodBase aCurrentMethodForLocals,
|
|
string aMethodName,
|
|
TypeInformation aTypeInfo,
|
|
bool aDebugMode,
|
|
IDictionary<string, object> aMethodData)
|
|
{
|
|
MethodInformation xMethodInfo;
|
|
{
|
|
MethodInformation.Variable[] xVars = new MethodInformation.Variable[0];
|
|
int xCurOffset = 0;
|
|
// todo:implement check for body
|
|
//if (aCurrentMethodForLocals.HasBody) {
|
|
MethodBody xBody = aCurrentMethodForLocals.GetMethodBody();
|
|
if (xBody != null)
|
|
{
|
|
xVars = new MethodInformation.Variable[xBody.LocalVariables.Count];
|
|
foreach (LocalVariableInfo xVarDef in xBody.LocalVariables)
|
|
{
|
|
int xVarSize = (int)GetFieldStorageSize(xVarDef.LocalType);
|
|
if ((xVarSize % 4) != 0)
|
|
{
|
|
xVarSize += 4 - (xVarSize % 4);
|
|
}
|
|
xVars[xVarDef.LocalIndex] = new MethodInformation.Variable(xCurOffset,
|
|
xVarSize,
|
|
!xVarDef.LocalType.IsValueType,
|
|
xVarDef.LocalType);
|
|
// todo: implement support for generic parameters?
|
|
//if (!(xVarDef.VariableType is GenericParameter)) {
|
|
RegisterType(xVarDef.LocalType);
|
|
//}
|
|
xCurOffset += xVarSize;
|
|
}
|
|
}
|
|
MethodInformation.Argument[] xArgs;
|
|
if (!aCurrentMethodForArguments.IsStatic)
|
|
{
|
|
ParameterInfo[] xParameters = aCurrentMethodForArguments.GetParameters();
|
|
xArgs = new MethodInformation.Argument[xParameters.Length + 1];
|
|
xCurOffset = 0;
|
|
uint xArgSize;
|
|
for (int i = xArgs.Length - 1; i > 0; i--)
|
|
{
|
|
ParameterInfo xParamDef = xParameters[i - 1];
|
|
xArgSize = GetFieldStorageSize(xParamDef.ParameterType);
|
|
if ((xArgSize % 4) != 0)
|
|
{
|
|
xArgSize += 4 - (xArgSize % 4);
|
|
}
|
|
MethodInformation.Argument.KindEnum xKind = MethodInformation.Argument.KindEnum.In;
|
|
if (xParamDef.IsOut)
|
|
{
|
|
if (xParamDef.IsIn)
|
|
{
|
|
xKind = MethodInformation.Argument.KindEnum.ByRef;
|
|
}
|
|
else
|
|
{
|
|
xKind = MethodInformation.Argument.KindEnum.Out;
|
|
}
|
|
}
|
|
xArgs[i] = new MethodInformation.Argument(xArgSize,
|
|
xCurOffset,
|
|
xKind,
|
|
!xParamDef.ParameterType.IsValueType,
|
|
GetTypeInfo(xParamDef.ParameterType),
|
|
xParamDef.ParameterType);
|
|
xCurOffset += (int)xArgSize;
|
|
}
|
|
xArgSize = 4;
|
|
// this
|
|
xArgs[0] = new MethodInformation.Argument(xArgSize,
|
|
xCurOffset,
|
|
MethodInformation.Argument.KindEnum.In,
|
|
!aCurrentMethodForArguments.DeclaringType.IsValueType,
|
|
GetTypeInfo(aCurrentMethodForArguments.DeclaringType),
|
|
aCurrentMethodForArguments.DeclaringType);
|
|
}
|
|
else
|
|
{
|
|
ParameterInfo[] xParameters = aCurrentMethodForArguments.GetParameters();
|
|
xArgs = new MethodInformation.Argument[xParameters.Length];
|
|
xCurOffset = 0;
|
|
for (int i = xArgs.Length - 1; i >= 0; i--)
|
|
{
|
|
ParameterInfo xParamDef = xParameters[i]; //xArgs.Length - i - 1];
|
|
uint xArgSize = GetFieldStorageSize(xParamDef.ParameterType);
|
|
if ((xArgSize % 4) != 0)
|
|
{
|
|
xArgSize += 4 - (xArgSize % 4);
|
|
}
|
|
MethodInformation.Argument.KindEnum xKind = MethodInformation.Argument.KindEnum.In;
|
|
if (xParamDef.IsOut)
|
|
{
|
|
if (xParamDef.IsIn)
|
|
{
|
|
xKind = MethodInformation.Argument.KindEnum.ByRef;
|
|
}
|
|
else
|
|
{
|
|
xKind = MethodInformation.Argument.KindEnum.Out;
|
|
}
|
|
}
|
|
xArgs[i] = new MethodInformation.Argument(xArgSize,
|
|
xCurOffset,
|
|
xKind,
|
|
!xParamDef.ParameterType.IsValueType,
|
|
GetTypeInfo(xParamDef.ParameterType),
|
|
xParamDef.ParameterType);
|
|
xCurOffset += (int)xArgSize;
|
|
}
|
|
}
|
|
int xResultSize = 0;
|
|
//= GetFieldStorageSize(aCurrentMethodForArguments.ReturnType.ReturnType);
|
|
MethodInfo xMethInfo = aCurrentMethodForArguments as MethodInfo;
|
|
Type xReturnType = typeof(void);
|
|
if (xMethInfo != null)
|
|
{
|
|
xResultSize = (int)GetFieldStorageSize(xMethInfo.ReturnType);
|
|
xReturnType = xMethInfo.ReturnType;
|
|
}
|
|
xMethodInfo = new MethodInformation(aMethodName,
|
|
xVars,
|
|
xArgs,
|
|
(uint)xResultSize,
|
|
!aCurrentMethodForArguments.IsStatic,
|
|
aTypeInfo,
|
|
aCurrentMethodForArguments,
|
|
xReturnType,
|
|
aDebugMode,
|
|
aMethodData);
|
|
}
|
|
return xMethodInfo;
|
|
}
|
|
|
|
public static Dictionary<string, TypeInformation.Field> GetTypeFieldInfo(MethodBase aCurrentMethod,
|
|
out uint aObjectStorageSize)
|
|
{
|
|
Type xCurrentInspectedType = aCurrentMethod.DeclaringType;
|
|
return GetTypeFieldInfo(xCurrentInspectedType,
|
|
out aObjectStorageSize);
|
|
}
|
|
|
|
private static void GetTypeFieldInfoImpl(List<KeyValuePair<string, TypeInformation.Field>> aTypeFields,
|
|
Type aType,
|
|
ref uint aObjectStorageSize)
|
|
{
|
|
Type xActualType = aType;
|
|
Dictionary<string, PlugFieldAttribute> xCurrentPlugFieldList = new Dictionary<string, PlugFieldAttribute>();
|
|
do
|
|
{
|
|
if (mCurrent.mPlugFields.ContainsKey(aType))
|
|
{
|
|
var xOrigList = mCurrent.mPlugFields[aType];
|
|
foreach (var item in xOrigList)
|
|
{
|
|
xCurrentPlugFieldList.Add(item.Key,
|
|
item.Value);
|
|
}
|
|
}
|
|
foreach (FieldInfo xField in aType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly))
|
|
{
|
|
if (xField.IsStatic)
|
|
{
|
|
continue;
|
|
}
|
|
//if (xField.HasConstant) {
|
|
// Console.WriteLine("Field is constant: " + xField.GetFullName());
|
|
//}
|
|
// todo: add support for constants?
|
|
PlugFieldAttribute xPlugFieldAttr = null;
|
|
if (xCurrentPlugFieldList.ContainsKey(xField.GetFullName()))
|
|
{
|
|
xPlugFieldAttr = xCurrentPlugFieldList[xField.GetFullName()];
|
|
xCurrentPlugFieldList.Remove(xField.GetFullName());
|
|
}
|
|
Type xFieldType = null;
|
|
int xFieldSize;
|
|
string xFieldId;
|
|
if (xPlugFieldAttr != null)
|
|
{
|
|
xFieldType = xPlugFieldAttr.FieldType;
|
|
xFieldId = xPlugFieldAttr.FieldId;
|
|
}
|
|
else
|
|
{
|
|
xFieldId = xField.GetFullName();
|
|
}
|
|
if (xFieldType == null)
|
|
{
|
|
xFieldType = xField.FieldType;
|
|
}
|
|
//if ((!xFieldType.IsValueType && aGCObjects && xFieldType.IsClass) || (xPlugFieldAttr != null && xPlugFieldAttr.IsExternalValue && aGCObjects)) {
|
|
// continue;
|
|
//}
|
|
if ((xFieldType.IsClass && !xFieldType.IsValueType) || (xPlugFieldAttr != null && xPlugFieldAttr.IsExternalValue))
|
|
{
|
|
xFieldSize = 4;
|
|
}
|
|
else
|
|
{
|
|
xFieldSize = (int)GetFieldStorageSize(xFieldType);
|
|
}
|
|
//}
|
|
if ((from item in aTypeFields
|
|
where item.Key == xFieldId
|
|
select item).Count() > 0)
|
|
{
|
|
continue;
|
|
}
|
|
int xOffset = (int)aObjectStorageSize;
|
|
FieldOffsetAttribute xOffsetAttrib = xField.GetCustomAttributes(typeof(FieldOffsetAttribute),
|
|
true).FirstOrDefault() as FieldOffsetAttribute;
|
|
if (xOffsetAttrib != null)
|
|
{
|
|
xOffset = xOffsetAttrib.Value;
|
|
}
|
|
else
|
|
{
|
|
aObjectStorageSize += (uint)xFieldSize;
|
|
xOffset = -1;
|
|
}
|
|
aTypeFields.Insert(0,
|
|
new KeyValuePair<string, TypeInformation.Field>(xField.GetFullName(),
|
|
new TypeInformation.Field(xFieldSize,
|
|
xFieldType.IsClass && !xFieldType.IsValueType,
|
|
xFieldType,
|
|
(xPlugFieldAttr != null && xPlugFieldAttr.IsExternalValue))
|
|
{
|
|
Offset = xOffset
|
|
}));
|
|
}
|
|
while (xCurrentPlugFieldList.Count > 0)
|
|
{
|
|
var xItem = xCurrentPlugFieldList.Values.First();
|
|
xCurrentPlugFieldList.Remove(xItem.FieldId);
|
|
Type xFieldType = xItem.FieldType;
|
|
int xFieldSize;
|
|
string xFieldId = xItem.FieldId;
|
|
if (xFieldType == null)
|
|
{
|
|
xFieldType = xItem.FieldType;
|
|
}
|
|
if (xFieldType == null)
|
|
{
|
|
Engine.mCurrent.OnDebugLog(LogSeverityEnum.Error, "Plugged field {0} not found! (On Type {1})", xItem.FieldId, aType.AssemblyQualifiedName);
|
|
}
|
|
if (xItem.IsExternalValue || (xFieldType.IsClass && !xFieldType.IsValueType))
|
|
{
|
|
xFieldSize = 4;
|
|
}
|
|
else
|
|
{
|
|
xFieldSize = (int)GetFieldStorageSize(xFieldType);
|
|
}
|
|
int xOffset = (int)aObjectStorageSize;
|
|
aObjectStorageSize += (uint)xFieldSize;
|
|
aTypeFields.Insert(0,
|
|
new KeyValuePair<string, TypeInformation.Field>(xItem.FieldId,
|
|
new TypeInformation.Field(xFieldSize,
|
|
xFieldType.IsClass && !xFieldType.IsValueType,
|
|
xFieldType,
|
|
xItem.IsExternalValue)));
|
|
}
|
|
if (aType.FullName != "System.Object" && aType.BaseType != null)
|
|
{
|
|
aType = aType.BaseType;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
public static Dictionary<string, TypeInformation.Field> GetTypeFieldInfo(Type aType,
|
|
out uint aObjectStorageSize)
|
|
{
|
|
var xTypeFields = new List<KeyValuePair<string, TypeInformation.Field>>();
|
|
aObjectStorageSize = 0;
|
|
GetTypeFieldInfoImpl(xTypeFields,
|
|
aType,
|
|
ref aObjectStorageSize);
|
|
if (aType.IsExplicitLayout)
|
|
{
|
|
var xStructLayout = aType.StructLayoutAttribute;
|
|
if (xStructLayout.Size == 0)
|
|
{
|
|
aObjectStorageSize = (uint)((from item in xTypeFields
|
|
let xSize = item.Value.Offset + item.Value.Size
|
|
orderby xSize descending
|
|
select xSize).FirstOrDefault());
|
|
}
|
|
else
|
|
{
|
|
aObjectStorageSize = (uint)xStructLayout.Size;
|
|
}
|
|
}
|
|
int xOffset = 0;
|
|
Dictionary<string, TypeInformation.Field> xResult = new Dictionary<string, TypeInformation.Field>();
|
|
foreach (var item in xTypeFields)
|
|
{
|
|
var xItem = item.Value;
|
|
if (item.Value.Offset == -1)
|
|
{
|
|
xItem.Offset = xOffset;
|
|
xOffset += xItem.Size;
|
|
}
|
|
xResult.Add(item.Key,
|
|
xItem);
|
|
}
|
|
return xResult;
|
|
}
|
|
|
|
private static Op GetOpFromType(Type aType, ILReader aReader, MethodInformation aMethodInfo)
|
|
{
|
|
return (Op)Activator.CreateInstance(aType, aReader, aMethodInfo);
|
|
}
|
|
|
|
public static void QueueStaticField(FieldInfo aField)
|
|
{
|
|
if (mCurrent == null)
|
|
{
|
|
throw new Exception("ERROR: No Current Engine found!");
|
|
}
|
|
using (mCurrent.mStaticFieldsLocker.AcquireReaderLock())
|
|
{
|
|
if (mCurrent.mStaticFields.ContainsKey(aField))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
using (mCurrent.mStaticFieldsLocker.AcquireWriterLock())
|
|
{
|
|
if (!mCurrent.mStaticFields.ContainsKey(aField))
|
|
{
|
|
mCurrent.mStaticFields.Add(aField,
|
|
new QueuedStaticFieldInformation());
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void QueueStaticField(string aAssembly,
|
|
string aType,
|
|
string aField,
|
|
out string aFieldName)
|
|
{
|
|
if (mCurrent == null)
|
|
{
|
|
throw new Exception("ERROR: No Current Engine found!");
|
|
}
|
|
Type xTypeDef = GetType(aAssembly,
|
|
aType);
|
|
var xFieldDef = xTypeDef.GetField(aField);
|
|
if (xFieldDef != null)
|
|
{
|
|
QueueStaticField(xFieldDef);
|
|
aFieldName = DataMember.GetStaticFieldName(xFieldDef);
|
|
return;
|
|
}
|
|
throw new Exception("Field not found!(" + String.Format("{0}/{1}/{2}",
|
|
aAssembly,
|
|
aType,
|
|
aField));
|
|
}
|
|
|
|
public static void QueueStaticField(FieldInfo aField,
|
|
out string aDataName)
|
|
{
|
|
if (mCurrent == null)
|
|
{
|
|
throw new Exception("ERROR: No Current Engine found!");
|
|
}
|
|
if (!aField.IsStatic)
|
|
{
|
|
throw new Exception("Cannot add an instance field to the StaticField queue!");
|
|
}
|
|
aDataName = DataMember.GetStaticFieldName(aField);
|
|
QueueStaticField(aField);
|
|
}
|
|
|
|
// MtW:
|
|
// Right now, we only support one engine at a time per AppDomain. This might be changed
|
|
// later. See for example NHibernate does this with the ICurrentSessionContext interface
|
|
public static void QueueMethod(MethodBase aMethod)
|
|
{
|
|
if (mCurrent == null)
|
|
{
|
|
throw new Exception("ERROR: No Current Engine found!");
|
|
}
|
|
if (mCurrent.ChangingInnerMethod != null)
|
|
mCurrent.ChangingInnerMethod.Invoke(aMethod.GetFullName());
|
|
//Doku: it is not complete..it should check if a method is from P/Invoked Namespace and it checks it has pluuged
|
|
/*string xInnerMethodName = aMethod.GetFullName();
|
|
string xPattern=" System.";
|
|
int xPosIn = xInnerMethodName.IndexOf(xPattern,0);
|
|
//get method name and namespace
|
|
string xNameSpaceAndMethod=xInnerMethodName.Substring(xPosIn+xPattern.Length);
|
|
if (xNameSpaceAndMethod.StartsWith("Globalization.") || xNameSpaceAndMethod.StartsWith("Net.") || xNameSpaceAndMethod.StartsWith("Reflection.") || xNameSpaceAndMethod.StartsWith("Xml."))
|
|
{
|
|
string xMethodSignature=xInnerMethodName.Replace('.','_');
|
|
xMethodSignature = xInnerMethodName.Replace(" ", "__");
|
|
|
|
//throw new Exception(xInnerMethodName);
|
|
}*/
|
|
if (!aMethod.IsStatic)
|
|
{
|
|
RegisterType(aMethod.DeclaringType);
|
|
}
|
|
using (mCurrent.mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
if (mCurrent.mMethods.ContainsKey(aMethod))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
using (mCurrent.mMethodsLocker.AcquireWriterLock())
|
|
{
|
|
if (!mCurrent.mMethods.ContainsKey(aMethod))
|
|
{
|
|
if (mCurrent.mMethods is ReadOnlyDictionary<MethodBase, QueuedMethodInformation>)
|
|
{
|
|
EmitDependencyGraphLine(false,
|
|
aMethod.GetFullName());
|
|
throw new Exception("Cannot queue " + aMethod.GetFullName());
|
|
}
|
|
EmitDependencyGraphLine(false,
|
|
aMethod.GetFullName());
|
|
mCurrent.mMethods.Add(aMethod,
|
|
new QueuedMethodInformation()
|
|
{
|
|
Processed = false,
|
|
PreProcessed = false,
|
|
Index = mCurrent.mMethods.Count
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
public static int GetMethodIdentifier(MethodBase aMethod)
|
|
{
|
|
QueueMethod(aMethod);
|
|
using (mCurrent.mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
return mCurrent.mMethods[aMethod].Index;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Registers_Old a type and returns the Type identifier
|
|
/// </summary>
|
|
/// <param name="aType"></param>
|
|
/// <returns></returns>
|
|
public static int RegisterType(Type aType)
|
|
{
|
|
if (aType == null)
|
|
{
|
|
throw new ArgumentNullException("aType");
|
|
}
|
|
if (mCurrent == null)
|
|
{
|
|
throw new Exception("ERROR: No Current Engine found!");
|
|
}
|
|
if (aType.IsArray || aType.IsPointer)
|
|
{
|
|
if (aType.IsArray && aType.GetArrayRank() != 1)
|
|
{
|
|
//throw new Exception("Multidimensional arrays are not yet supported!");
|
|
}
|
|
if (aType.IsArray)
|
|
{
|
|
aType = typeof(Array);
|
|
}
|
|
else
|
|
{
|
|
aType = aType.GetElementType();
|
|
}
|
|
}
|
|
using (mCurrent.mTypesLocker.AcquireReaderLock())
|
|
{
|
|
var xItem = mCurrent.mTypes.FirstOrDefault(x => x.FullName.Equals(aType.FullName));
|
|
if (xItem != null)
|
|
{
|
|
return mCurrent.mTypes.IndexOf(xItem);
|
|
}
|
|
}
|
|
Type xFoundItem;
|
|
using (mCurrent.mTypesLocker.AcquireWriterLock())
|
|
{
|
|
xFoundItem = mCurrent.mTypes.FirstOrDefault(x => x.FullName.Equals(aType.FullName));
|
|
|
|
if (xFoundItem == null)
|
|
{
|
|
mCurrent.mTypes.Add(aType);
|
|
if (aType.FullName != "System.Object" && aType.BaseType != null)
|
|
{
|
|
Type xCurInspectedType = aType.BaseType;
|
|
RegisterType(xCurInspectedType);
|
|
}
|
|
return RegisterType(aType);
|
|
}
|
|
else
|
|
{
|
|
return mCurrent.mTypes.IndexOf(xFoundItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
//public static Assembly GetCrawledAssembly()
|
|
//{
|
|
// if (mCurrent == null)
|
|
// {
|
|
// throw new Exception("ERROR: No Current Engine found!");
|
|
// }
|
|
// return mCurrent.mCrawledAssembly;
|
|
//}
|
|
|
|
public static void QueueMethod2(string aAssembly,
|
|
string aType,
|
|
string aMethod)
|
|
{
|
|
MethodBase xMethodDef;
|
|
QueueMethod2(aAssembly,
|
|
aType,
|
|
aMethod,
|
|
out xMethodDef);
|
|
}
|
|
|
|
public static void QueueMethod2(string aAssembly,
|
|
string aType,
|
|
string aMethod,
|
|
out MethodBase aMethodDef)
|
|
{
|
|
Type xTypeDef = GetType(aAssembly,
|
|
aType);
|
|
// todo: find a way to specify one overload of a method
|
|
int xCount = 0;
|
|
aMethodDef = null;
|
|
foreach (MethodBase xMethodDef in xTypeDef.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
|
|
{
|
|
if (xMethodDef.Name == aMethod)
|
|
{
|
|
QueueMethod(xMethodDef);
|
|
if (aMethodDef == null)
|
|
{
|
|
aMethodDef = xMethodDef;
|
|
}
|
|
xCount++;
|
|
}
|
|
}
|
|
foreach (MethodBase xMethodDef in xTypeDef.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
|
|
{
|
|
if (xMethodDef.Name == aMethod)
|
|
{
|
|
QueueMethod(xMethodDef);
|
|
xCount++;
|
|
}
|
|
}
|
|
if (xCount == 0)
|
|
{
|
|
throw new Exception("Method '" + aType + "." + aMethod + "' not found in assembly '" + aAssembly + "'!");
|
|
}
|
|
}
|
|
|
|
public event DebugLogHandler DebugLog
|
|
{
|
|
add
|
|
{
|
|
mDebugLog += value;
|
|
}
|
|
remove
|
|
{
|
|
mDebugLog -= value;
|
|
}
|
|
}
|
|
|
|
private void OnDebugLog(LogSeverityEnum aSeverity,
|
|
string aMessage,
|
|
params object[] args)
|
|
{
|
|
if (mDebugLog != null)
|
|
{
|
|
mDebugLog(aSeverity,
|
|
String.Format(aMessage,
|
|
args));
|
|
}
|
|
}
|
|
|
|
private SortedList<string, Assembly> mAssemblyDefCache = new SortedList<string, Assembly>();
|
|
|
|
public static Type GetType(string aAssembly,
|
|
string aType)
|
|
{
|
|
Assembly xAssemblyDef;
|
|
if (mCurrent.mAssemblyDefCache.ContainsKey(aAssembly))
|
|
{
|
|
xAssemblyDef = mCurrent.mAssemblyDefCache[aAssembly];
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Assembly xAssembly = (from item in AppDomain.CurrentDomain.GetAssemblies()
|
|
// where item.FullName == aAssembly || item.GetName().Name == aAssembly
|
|
// select item).FirstOrDefault();
|
|
// if (xAssembly == null) {
|
|
// if (String.IsNullOrEmpty(aAssembly) || aAssembly == typeof(Engine).Assembly.GetName().Name || aAssembly == typeof(Engine).Assembly.GetName().FullName) {
|
|
// xAssembly = typeof(Engine).Assembly;
|
|
// }
|
|
// }
|
|
// if (xAssembly != null) {
|
|
// if (aAssembly.StartsWith("mscorlib"))
|
|
// throw new Exception("Shouldn't be used!");
|
|
// Console.WriteLine("Using AssemblyFactory for '{0}'", aAssembly);
|
|
// xAssemblyDef = AssemblyFactory.GetAssembly(xAssembly.Location);
|
|
// } else {
|
|
// xAssemblyDef = mCurrent.mCrawledAssembly.Resolver.Resolve(aAssembly);
|
|
// }
|
|
// mCurrent.mAssemblyDefCache.Add(aAssembly, xAssemblyDef);
|
|
if (String.IsNullOrEmpty(aAssembly) || aAssembly == typeof(Engine).Assembly.GetName().Name || aAssembly == typeof(Engine).Assembly.GetName().FullName)
|
|
{
|
|
aAssembly = typeof(Engine).Assembly.FullName;
|
|
}
|
|
xAssemblyDef = Assembly.Load(aAssembly);
|
|
}
|
|
return GetType(xAssemblyDef,
|
|
aType);
|
|
}
|
|
|
|
public static Type GetType(Assembly aAssembly,
|
|
string aType)
|
|
{
|
|
if (mCurrent == null)
|
|
{
|
|
throw new Exception("ERROR: No Current Engine found!");
|
|
}
|
|
string xActualTypeName = aType;
|
|
if (xActualTypeName.Contains("<") && xActualTypeName.Contains(">"))
|
|
{
|
|
xActualTypeName = xActualTypeName.Substring(0,
|
|
xActualTypeName.IndexOf("<"));
|
|
}
|
|
Type xResult = aAssembly.GetType(aType,
|
|
false);
|
|
if (xResult != null)
|
|
{
|
|
RegisterType(xResult);
|
|
return xResult;
|
|
}
|
|
throw new Exception("Type '" + aType + "' not found in assembly '" + aAssembly + "'!");
|
|
}
|
|
|
|
public static MethodBase GetMethodBase(Type aType,
|
|
string aMethod,
|
|
params string[] aParamTypes)
|
|
{
|
|
foreach (MethodBase xMethod in aType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
|
|
{
|
|
if (xMethod.Name != aMethod)
|
|
{
|
|
continue;
|
|
}
|
|
ParameterInfo[] xParams = xMethod.GetParameters();
|
|
if (xParams.Length != aParamTypes.Length)
|
|
{
|
|
continue;
|
|
}
|
|
bool errorFound = false;
|
|
for (int i = 0; i < xParams.Length; i++)
|
|
{
|
|
if (xParams[i].ParameterType.FullName != aParamTypes[i])
|
|
{
|
|
errorFound = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!errorFound)
|
|
{
|
|
return xMethod;
|
|
}
|
|
}
|
|
foreach (MethodBase xMethod in aType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public))
|
|
{
|
|
if (xMethod.Name != aMethod)
|
|
{
|
|
continue;
|
|
}
|
|
ParameterInfo[] xParams = xMethod.GetParameters();
|
|
if (xParams.Length != aParamTypes.Length)
|
|
{
|
|
continue;
|
|
}
|
|
bool errorFound = false;
|
|
for (int i = 0; i < xParams.Length; i++)
|
|
{
|
|
if (xParams[i].ParameterType.FullName != aParamTypes[i])
|
|
{
|
|
errorFound = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!errorFound)
|
|
{
|
|
return xMethod;
|
|
}
|
|
}
|
|
throw new Exception("Method not found!");
|
|
}
|
|
public static IEnumerable<Assembly> GetAllAssemblies()
|
|
{
|
|
using (mCurrent.mMethodsLocker.AcquireReaderLock())
|
|
{
|
|
return (from item in mCurrent.mMethods.Keys
|
|
select item.DeclaringType.Module.Assembly).Distinct(new AssemblyEqualityComparer()).ToArray();
|
|
}
|
|
}
|
|
|
|
private int mInstructionsToSkip = 0;
|
|
|
|
public static void SetInstructionsToSkip(int aCount)
|
|
{
|
|
if (mCurrent == null)
|
|
{
|
|
throw new Exception("No Current Engine!");
|
|
}
|
|
mCurrent.mInstructionsToSkip = aCount;
|
|
}
|
|
|
|
#region Dependency graph code
|
|
|
|
private static bool mEmitDependencyGraph = false;
|
|
|
|
public static void EmitDependencyGraphLine(bool aIsContainer, string aMessage)
|
|
{
|
|
}
|
|
|
|
static Engine()
|
|
{
|
|
mEmitDependencyGraph = Environment.GetEnvironmentVariables().Contains("CosmosDependencyGraph") || Environment.MachineName.Equals("laptop-matthijs",
|
|
StringComparison.InvariantCultureIgnoreCase);
|
|
if (mEmitDependencyGraph)
|
|
{
|
|
File.Delete(@"d:\dependencygraph.txt");
|
|
}
|
|
RunningOnMono = Type.GetType("Mono.Runtime") != null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
public static readonly bool RunningOnMono;
|
|
}
|
|
}
|