Cosmos/source/Indy.IL2CPU/DynamicMethodEmit.cs
2009-05-11 00:56:07 +00:00

406 lines
16 KiB
C#

using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;
namespace Indy.IL2CPU
{
/// <summary>
/// Provides Compile-Time Service which enables the emission of dynamic methods.
/// </summary>
/// <remarks>
/// If a method has a variable number (or type) of parameters which depend on its context,
/// this feature can be used to improve code-reuse and optimize performance.
/// </remarks>
/// <example>
/// To create a dynamic method emitter, one must create two classes derived from:
/// CEmittedMethodInfo
/// CMethodEmitterInfo
/// And register the CMethodEmitterInfo in the DynamicMethodEmit constructior.
/// </example>
public class DynamicMethodEmit
{
/// <summary>
/// Provides information about an emitted method, including the ability to find if
/// that particular emitted method overides another method.
/// </summary>
public abstract class CEmittedMethodInfo
{
/// <summary>
/// Returns true if the emitted method can override the provided method.
/// </summary>
/// <param name="method">The method to check overridability for.</param>
/// <returns>True, if this method is suitable, otherwise, false.</returns>
public abstract bool GetHandlesMethod(MethodBase method);
/// <summary>
/// Gets the MethodInfo of the emitted method which can overide the provided method.
/// </summary>
/// <param name="method">The method to retrieve an overide for.</param>
/// <returns>The MethodInfo of the emitted method which can overide the provided method.</returns>
public abstract MethodInfo GetHandlerForMethod(MethodBase method);
}
/// <summary>
/// Provides information about a method emitter, including the ability to find if that
/// particular method emitter can emit a method overides another method.
/// </summary>
public abstract class CMethodEmitterInfo
{
/// <summary>
/// Returns true if this emitter can emit a method which overides the provided method.
/// </summary>
/// <param name="method">The method to check overrideability of.</param>
/// <returns>True, if this emitter can emit a method which overides the provided method, otherwise, false.</returns>
public abstract bool GetEmittsMethod(MethodBase method);
/// <summary>
/// Emitts a method which can overide the provided method.
/// </summary>
/// <param name="method">The method to overide.</param>
/// <returns>The MethodInfo of the emitted method which can override the provided method.</returns>
public abstract MethodInfo EmitMethod(MethodBase method);
}
/// <summary>
/// CEmittedMethodInfo for Multidimensional Arrays
/// </summary>
public class MAMGroup : CEmittedMethodInfo
{
public Type arrayType;
public int Ranks;
public MethodInfo mbctor;
public MethodInfo mbget;
public MethodInfo mbset;
public MethodInfo mbaddr;
public MAMGroup(Type t, int r, MethodInfo c, MethodInfo g, MethodInfo s, MethodInfo a)
{
arrayType = t;
Ranks = r;
mbctor = c;
mbget = g;
mbset = s;
mbaddr = a;
}
public override bool GetHandlesMethod(MethodBase method)
{
return (
(method.DeclaringType.FullName.Split('[')[0] == arrayType.FullName) &&
(method.DeclaringType.GetArrayRank() == Ranks)
);
}
public override MethodInfo GetHandlerForMethod(MethodBase method)
{
switch (method.Name)
{
case ".ctor":
return mbctor;
case "Get":
return mbget;
case "Set":
return mbset;
case "Address":
return mbaddr;
default:
throw new NotImplementedException("MultiArray method '" + method.Name + "' is not defined!");
}
}
}
/// <summary>
/// CMethodEmitterInfo for Multidimensional Arrays
/// </summary>
public class MAMEmitter : CMethodEmitterInfo
{
private static void EmitRecursiveFillArray(int ranknum, int maxranks, Type arrayType, ILGenerator EmitIL)
{
byte lCPA = (byte)(ranknum * 2);
byte lCRN = (byte)(lCPA + 1);
byte lCAP = (byte)(lCPA + 2);
EmitIL.Emit(OpCodes.Ldarg_S, (byte)(ranknum + 1));
EmitIL.Emit(OpCodes.Stloc_S, lCRN);
Label Lb_CreateArray = EmitIL.DefineLabel();
EmitIL.MarkLabel(Lb_CreateArray);
EmitIL.Emit(OpCodes.Ldloc_S, lCRN);
EmitIL.Emit(OpCodes.Ldc_I4_1);
EmitIL.Emit(OpCodes.Sub);
EmitIL.Emit(OpCodes.Stloc_S, lCRN);
EmitIL.Emit(OpCodes.Ldarg_S, (byte)(ranknum + 2));
if (ranknum == (maxranks - 1))
{
EmitIL.Emit(OpCodes.Newarr, arrayType);
}
else
{
EmitIL.Emit(OpCodes.Newarr, typeof(int[]));
}
EmitIL.Emit(OpCodes.Stloc_S, lCAP);
EmitIL.Emit(OpCodes.Ldloc_S, lCPA);
EmitIL.Emit(OpCodes.Ldloc_S, lCRN);
EmitIL.Emit(OpCodes.Ldloc_S, lCAP);
EmitIL.Emit(OpCodes.Stelem, typeof(int[]));
if (ranknum < (maxranks - 1))
EmitRecursiveFillArray(ranknum + 1, maxranks, arrayType, EmitIL);
EmitIL.Emit(OpCodes.Ldloc_S, lCRN);
EmitIL.Emit(OpCodes.Brtrue, Lb_CreateArray);
}
private static void EmitWalkArrays(int Ranks, ILGenerator EmitIL)
{
EmitIL.Emit(OpCodes.Ldarg_0);
for (int i = 1;i < Ranks;i++)
{
EmitIL.Emit(OpCodes.Ldarg_S, (byte)i);
EmitIL.Emit(OpCodes.Ldelem, typeof(int[]));
}
EmitIL.Emit(OpCodes.Ldarg_S, (byte)Ranks);
}
public static void Emit_MultiArray_Ctor(Type arrayType, int Ranks)
{
String MethodName = "Emit" + (Instance.EmitCount++);
MethodBuilder EmitMeth = Instance.EmitContType.DefineMethod(MethodName, MethodAttributes.Public | MethodAttributes.Static);
Type[] MethodParams = new Type[Ranks + 1];
MethodParams[0] = arrayType.MakeArrayType(Ranks);
for (int i = 1;i <= Ranks;i++)
MethodParams[i] = typeof(int);
EmitMeth.SetParameters(MethodParams);
ILGenerator EmitIL = EmitMeth.GetILGenerator();
for (int i = 1;i < Ranks;i++)
{
EmitIL.DeclareLocal(typeof(int[]));
EmitIL.DeclareLocal(typeof(int));
}
EmitIL.DeclareLocal(typeof(int[]));
EmitIL.Emit(OpCodes.Ldarg_0);
EmitIL.Emit(OpCodes.Stloc_0);
EmitRecursiveFillArray(0, Ranks - 1, arrayType, EmitIL);
EmitIL.Emit(OpCodes.Ret);
}
public static void Emit_MultiArray_Get(Type arrayType, int Ranks)
{
String MethodName = "Emit" + (Instance.EmitCount++);
MethodBuilder EmitMeth = Instance.EmitContType.DefineMethod(MethodName, MethodAttributes.Public | MethodAttributes.Static);
Type[] MethodParams = new Type[Ranks + 1];
MethodParams[0] = arrayType.MakeArrayType(Ranks);
for (int i = 1;i <= Ranks;i++)
MethodParams[i] = typeof(int);
EmitMeth.SetParameters(MethodParams);
EmitMeth.SetReturnType(arrayType);
ILGenerator EmitIL = EmitMeth.GetILGenerator();
EmitWalkArrays(Ranks, EmitIL);
EmitIL.Emit(OpCodes.Ldelem, arrayType);
EmitIL.Emit(OpCodes.Ret);
}
public static void Emit_MultiArray_Set(Type arrayType, int Ranks)
{
String MethodName = "Emit" + (Instance.EmitCount++);
MethodBuilder EmitMeth = Instance.EmitContType.DefineMethod(MethodName, MethodAttributes.Public | MethodAttributes.Static);
Type[] MethodParams = new Type[Ranks + 2];
MethodParams[0] = arrayType.MakeArrayType(Ranks);
for (int i = 1;i <= Ranks;i++)
MethodParams[i] = typeof(int);
MethodParams[Ranks + 1] = arrayType;
EmitMeth.SetParameters(MethodParams);
ILGenerator EmitIL = EmitMeth.GetILGenerator();
EmitWalkArrays(Ranks, EmitIL);
EmitIL.Emit(OpCodes.Ldarg_S, (byte)(Ranks + 1));
EmitIL.Emit(OpCodes.Stelem, arrayType);
EmitIL.Emit(OpCodes.Ret);
}
public static void Emit_MultiArray_Address(Type arrayType, int Ranks)
{
String MethodName = "Emit" + (Instance.EmitCount++);
MethodBuilder EmitMeth = Instance.EmitContType.DefineMethod(MethodName, MethodAttributes.Public | MethodAttributes.Static);
Type[] MethodParams = new Type[Ranks + 1];
MethodParams[0] = arrayType.MakeArrayType(Ranks);
for (int i = 1;i <= Ranks;i++)
MethodParams[i] = typeof(int);
EmitMeth.SetParameters(MethodParams);
EmitMeth.SetReturnType(arrayType.MakePointerType());
ILGenerator EmitIL = EmitMeth.GetILGenerator();
EmitWalkArrays(Ranks - 1, EmitIL);
EmitIL.Emit(OpCodes.Ldelem, arrayType.MakePointerType());
EmitIL.Emit(OpCodes.Ret);
}
public override bool GetEmittsMethod(MethodBase method)
{
return (
(method.DeclaringType.IsArray) &&
(method.DeclaringType.GetArrayRank() > 1)
);
}
public override MethodInfo EmitMethod(MethodBase method)
{
Type arrayType = Type.GetType(method.DeclaringType.AssemblyQualifiedName.Split('[')[0] + method.DeclaringType.AssemblyQualifiedName.Split(']')[1]);
int Ranks = method.DeclaringType.GetArrayRank();
BeginType();
Emit_MultiArray_Ctor(arrayType, Ranks);
Emit_MultiArray_Get(arrayType, Ranks);
Emit_MultiArray_Set(arrayType, Ranks);
Emit_MultiArray_Address(arrayType, Ranks);
MethodInfo[] mbs = EndType();
Instance.CEMIs.Add(new MAMGroup(arrayType, Ranks, mbs[0], mbs[1], mbs[2], mbs[3]));
return GetDynamicMethod(method);
}
}
/// <summary>
/// Operating instance of the DynamicMethodEmit class.
/// </summary>
public static DynamicMethodEmit Instance = new DynamicMethodEmit(AppDomain.CurrentDomain);
/// <summary>
/// AppDomain into which emission is occuring.
/// </summary>
public readonly AppDomain EmitDomain;
/// <summary>
/// Assembly into which emission is occuring.
/// </summary>
public readonly AssemblyBuilder EmitAssm;
/// <summary>
/// Module into which emission is occuring.
/// </summary>
public readonly ModuleBuilder EmitModu;
/// <summary>
/// Container Type of the methods which are currently being emitted.
/// </summary>
public TypeBuilder EmitContType;
/// <summary>
/// Number of Emitted menthods in the current Container Type.
/// </summary>
private int EmitCount = 0;
/// <summary>
/// Current number of Container Types.
/// </summary>
private int TypeCount = 0;
/// <summary>
/// List of all the currently emitted methods.
/// </summary>
/// <remarks>
/// If a CEmittedMethodInfo is to be used, it must be present in this list.
/// </remarks>
List<CEmittedMethodInfo> CEMIs = new List<CEmittedMethodInfo>();
/// <summary>
/// List of all the method emitters.
/// </summary>
/// <remarks>
/// If a CMethodEmitterInfo is to be used, it must be present in this list.
/// </remarks>
List<CMethodEmitterInfo> CMEIs = new List<CMethodEmitterInfo>();
public DynamicMethodEmit(AppDomain aEmitDomain)
{
EmitDomain = aEmitDomain;
EmitAssm = EmitDomain.DefineDynamicAssembly(new AssemblyName() { Name = "IndyIL2CPU_EmitAssm" }, AssemblyBuilderAccess.RunAndSave);
EmitModu = EmitAssm.DefineDynamicModule("IndyIL2CPU_EmitAssm");
//Register CMethodEmitterInfos Here
CMEIs.Add(new MAMEmitter()); //Multidimensional Arrays
Instance = this;
}
/// <summary>
/// Begins a type to which emission will occur.
/// After all desired methods have been emitted,
/// call EndType() to get the MethodInfos.
/// </summary>
public static void BeginType()
{
Instance.EmitContType = Instance.EmitModu.DefineType(
"Indy.IL2CPU.MultiArrayEmit.ContType" + (Instance.TypeCount++),
TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit
);
Instance.EmitCount = 0;
}
/// <summary>
/// Finializes emission of a type.
/// </summary>
/// <returns>MethodInfos of the emitted methods.</returns>
public static MethodInfo[] EndType()
{
Type t = Instance.EmitContType.CreateType();
MethodInfo[] mbs = new MethodInfo[Instance.EmitCount];
for (int i = 0;i < Instance.EmitCount;i++)
mbs[i] = t.GetMethod("Emit" + i);
return mbs;
}
/// <summary>
/// Returns true if DynamicMethodEmit Instance currently contains an emitter which can overide the provided Method.
/// <param name="method">The method to search for an overide emitter for.</param>
/// <returns>True if a suitable emitter was found, otherwise, false.</returns>
public static bool GetHasDynamicMethod(MethodBase method)
{
foreach (CMethodEmitterInfo cmei in Instance.CMEIs)
{
if (cmei.GetEmittsMethod(method))
return true;
}
return false;
}
/// <summary>
/// Returns the MethodInfo of the method which can overide the provided method,
/// generating it if nessecary.
/// </summary>
/// <param name="method">The method to overide.</param>
/// <returns>The MethodInfo of the suitable override.</returns>
/// <exception cref="System.NotImplementedException">
/// Thrown if a sutiable override could not be found or generated.
/// </exception>
public static MethodInfo GetDynamicMethod(MethodBase method)
{
foreach (CEmittedMethodInfo cemi in Instance.CEMIs)
{
if (cemi.GetHandlesMethod(method))
{
return cemi.GetHandlerForMethod(method);
}
}
foreach (CMethodEmitterInfo cmei in Instance.CMEIs)
{
if (cmei.GetEmittsMethod(method))
{
return cmei.EmitMethod(method);
}
}
throw new NotImplementedException();
}
}
}