Cosmos/source/Indy.IL2CPU/Compiler/CompilerHelper.cs

357 lines
No EOL
14 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Globalization;
using System.Xml;
using System.Diagnostics;
using Indy.IL2CPU.IL;
namespace Indy.IL2CPU.Compiler
{
public class CompilerHelper
{
public class CacheState: IComparable<CacheState>, IEquatable<CacheState> {
public List<string> UsedGenericMethods = new List<string>();
public List<string> UsedGenericTypes = new List<string>();
internal void Load(string aFile)
{
if (!File.Exists(aFile))
{
return;
}
var xDoc = new XmlDocument();
xDoc.Load(aFile);
var xElemMethods = xDoc.SelectSingleNode("/CacheState/GenericMethods");
foreach (XmlNode xMethodNode in xElemMethods.ChildNodes)
{
UsedGenericMethods.Add(xMethodNode.InnerText);
}
var xElemTypes = xDoc.SelectSingleNode("/CacheState/GenericTypes");
foreach (XmlNode xTypeNode in xElemTypes.ChildNodes)
{
UsedGenericTypes.Add(xTypeNode.InnerText);
}
UsedGenericTypes.Sort();
UsedGenericMethods.Sort();
}
internal void Save(string aFile)
{
using (var xOut = XmlWriter.Create(aFile))
{
xOut.WriteStartDocument();
xOut.WriteStartElement("CacheState");
{
xOut.WriteStartElement("GenericMethods");
{
foreach (var xMethod in UsedGenericMethods)
{
xOut.WriteStartElement("Method");
{
xOut.WriteCData(xMethod);
}
xOut.WriteEndElement();
}
} xOut.WriteEndElement();
xOut.WriteStartElement("GenericTypes");
{
foreach (var xType in UsedGenericTypes)
{
xOut.WriteStartElement("Type");
{
xOut.WriteCData(xType);
}
xOut.WriteEndElement();
}
} xOut.WriteEndElement();
}
xOut.WriteEndElement();
xOut.WriteEndDocument();
}
}
#region IComparable<CacheState> Members
public int CompareTo(CacheState other)
{
var xResult = this.UsedGenericMethods.Count.CompareTo(other.UsedGenericMethods.Count);
if (xResult != 0)
{
return xResult;
}
xResult = this.UsedGenericTypes.Count.CompareTo(other.UsedGenericTypes.Count);
if (xResult != 0)
{
return xResult;
}
for (int i = 0; i < UsedGenericMethods.Count; i++)
{
xResult = UsedGenericMethods[i].CompareTo(other.UsedGenericMethods[i]);
if (xResult != 0)
{
return xResult;
}
}
for (int i = 0; i < UsedGenericTypes.Count; i++)
{
xResult = UsedGenericTypes[i].CompareTo(other.UsedGenericTypes[i]);
if (xResult != 0)
{
return xResult;
}
}
return 0;
}
#endregion
#region IEquatable<CacheState> Members
public bool Equals(CacheState other)
{
return CompareTo(other) == 0;
}
#endregion
}
public List<Assembly> SkipList
{
get;
set;
}
public event Func<Assembly, string> GetCacheStateFile;
public event Func<Assembly, string> GetChecksumFile;
public event Func<Assembly, bool, Assembler.Assembler> GetAssembler;
public event Action<Assembly, Assembler.Assembler> SaveAssembler;
public event Func<OpCodeMap> GetOpCodeMap;
public event Action<LogSeverityEnum, string> DebugLog;
public List<string> Plugs = new List<string>();
public CompilerHelper()
{
SkipList = new List<Assembly>();
}
private Assembly mEntryAssembly;
private IEnumerable<Assembly> mAllAssemblies;
public void CompileExe(Assembly aAssembly)
{
if (aAssembly == null)
{
throw new ArgumentNullException("aAssembly");
}
mEntryAssembly=aAssembly;
mAllAssemblies = Scanner.GetAllAssemblies(aAssembly, SkipList);
var xAssembliesToScanForGenerics = GetAssembliesToScanForGenerics(mAllAssemblies);
LoadCacheStates(mAllAssemblies);
bool xNeedsFullRecompile = false;
// now scan for generics usage
foreach (var xAsm in xAssembliesToScanForGenerics)
{
var xTest = this.ScanAssembly(xAsm);
if(!xTest.Equals(mCacheStates[xAsm])){
xNeedsFullRecompile =true;
}
}
if (xNeedsFullRecompile)
{
DoFullRecompile();
}
else
{
SmartRecompile();
}
}
private void CompileAssembly(Assembly aAssembly)
{
Console.Write(new String('-', Console.BufferWidth));
Console.WriteLine("Starting compilation of assembly: " + aAssembly.GetName().Name);
var xCompiler = new AssemblyCompiler();
xCompiler.DebugLog = delegate(LogSeverityEnum aSeverity, string aMessage) { this.DebugLog(aSeverity, aMessage); };
xCompiler.IsEntrypointAssembly = aAssembly == mEntryAssembly;
xCompiler.Assembler = GetAssembler(aAssembly, xCompiler.IsEntrypointAssembly);
xCompiler.Assembly = aAssembly;
if (xCompiler.IsEntrypointAssembly)
{
foreach (var xRef in mAllAssemblies)
{
xCompiler.AssemblyReferences.Add(xRef.GetName().FullName);
}
}
// todo: move this out to a property/event
xCompiler.OpCodeMap = GetOpCodeMap();
xCompiler.Plugs.AddRange(Plugs);
xCompiler.Types.AddRange(mGenericTypeInstancesToGenerate[aAssembly]);
xCompiler.Methods.AddRange(mGenericMethodInstancesToGenerate[aAssembly]);
xCompiler.Execute();
SaveAssembler(aAssembly, xCompiler.Assembler);
}
private void DoFullRecompile()
{
foreach (var xAsm in mAllAssemblies)
{
CompileAssembly(xAsm);
}
}
private void SmartRecompile()
{
foreach (var xAsm in GetAssembliesToScanForGenerics(mAllAssemblies))
{
CompileAssembly(xAsm);
}
}
private void LoadCacheStates(IEnumerable<Assembly> xAllAssemblies)
{
mCacheStates.Clear();
foreach (var xAsm in xAllAssemblies)
{
var xState = new CacheState();
xState.Load(GetCacheStateFile(xAsm));
mCacheStates.Add(xAsm, xState);
}
}
private Dictionary<Assembly, CacheState> mCacheStates = new Dictionary<Assembly, CacheState>();
private Dictionary<Assembly, List<Type>> mGenericTypeInstancesToGenerate = new Dictionary<Assembly, List<Type>>();
private Dictionary<Assembly, List<MethodBase>> mGenericMethodInstancesToGenerate = new Dictionary<Assembly, List<MethodBase>>();
private CacheState ScanAssembly(Assembly aAsm)
{
var xResult = new CacheState();
bool xShouldSkipMain = aAsm == mEntryAssembly;
if (!mGenericTypeInstancesToGenerate.ContainsKey(aAsm))
{
mGenericTypeInstancesToGenerate.Add(aAsm, new List<Type>());
}
var xDeclaringType_TypeInstances = mGenericTypeInstancesToGenerate[aAsm];
if (!mGenericMethodInstancesToGenerate.ContainsKey(aAsm))
{
mGenericMethodInstancesToGenerate.Add(aAsm, new List<MethodBase>());
}
var xDeclaringType_MethodInstances = mGenericMethodInstancesToGenerate[aAsm];
Action<Type> xCheckType = null;
xCheckType = new Action<Type>(delegate(Type aType)
{
if (aType.IsGenericType && !aType.IsGenericTypeDefinition)
{
// add to the list of the current assembly
if (!xResult.UsedGenericTypes.Contains(aType.AssemblyQualifiedName))
{
xResult.UsedGenericTypes.Add(aType.AssemblyQualifiedName);
}
// add to the list of the declaring assembly, so it can generate it later on
if (!xDeclaringType_TypeInstances.Contains(aType))
{
xDeclaringType_TypeInstances.Add(aType);
}
foreach (var xArg in aType.GetGenericArguments())
{
xCheckType(xArg);
}
}
});
foreach (var xType in aAsm.GetTypes())
{
if (xType.BaseType != null)
{
xCheckType(xType.BaseType);
}
foreach (var xMethod in xType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly))
{
if (xShouldSkipMain && xMethod == aAsm.EntryPoint)
{
continue;
}
xCheckType(xMethod.ReturnType);
foreach (var xParam in xMethod.GetParameters())
{
xCheckType(xParam.ParameterType);
}
try
{
if (xMethod.GetMethodBody() == null)
{
continue;
}
}
catch (System.Security.VerificationException VE)
{
// apparently, ms uses some scary code for the .net framework..
continue;
}
catch (Exception E)
{
throw;
}
var xReader = new ILReader(xMethod);
while (xReader.Read())
{
switch (xReader.OpCode)
{
case OpCodeEnum.Call:
case OpCodeEnum.Callvirt:
case OpCodeEnum.Newobj:
xCheckType(xReader.OperandValueMethod.DeclaringType);
if (xReader.OperandValueMethod.IsGenericMethod && !xReader.OperandValueMethod.IsGenericMethodDefinition)
{
var xName = xReader.OperandValueMethod.GetFullName();
if (!xResult.UsedGenericMethods.Contains(xName))
{
xResult.UsedGenericMethods.Add(xName);
}
// add to the list of the declaring assembly, so it can generate it later on
if (!xDeclaringType_MethodInstances.Contains(xReader.OperandValueMethod))
{
xDeclaringType_MethodInstances.Add(xReader.OperandValueMethod);
}
}
break;
}
}
}
}
return xResult;
}
private List<Assembly> GetAssembliesToScanForGenerics(IEnumerable<Assembly> xAllAssemblies)
{
var xAssembliesToScanForGenerics = new List<Assembly>();
foreach (var xAsm in xAllAssemblies)
{
var xLastTime = File.GetLastWriteTimeUtc(xAsm.Location).ToBinary();
var xChecksumFileName = GetChecksumFile(xAsm);
if (File.Exists(xChecksumFileName))
{
var xChecksumStr = File.ReadAllText(xChecksumFileName);
long xChecksum;
if (Int64.TryParse(xChecksumStr, out xChecksum))
{
if (xChecksum == xLastTime)
{
continue;
}
}
}
xAssembliesToScanForGenerics.Add(xAsm);
}
return xAssembliesToScanForGenerics;
}
}
}