using System; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Collections.Generic; namespace Indy.IL2CPU { /// /// Provides Compile-Time Service which enables the emission of dynamic methods. /// /// /// 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. /// /// /// To create a dynamic method emitter, one must create two classes derived from: /// CEmittedMethodInfo /// CMethodEmitterInfo /// And register the CMethodEmitterInfo in the DynamicMethodEmit constructior. /// public class DynamicMethodEmit { /// /// Provides information about an emitted method, including the ability to find if /// that particular emitted method overides another method. /// public abstract class CEmittedMethodInfo { /// /// Returns true if the emitted method can override the provided method. /// /// The method to check overridability for. /// True, if this method is suitable, otherwise, false. public abstract bool GetHandlesMethod(MethodBase method); /// /// Gets the MethodInfo of the emitted method which can overide the provided method. /// /// The method to retrieve an overide for. /// The MethodInfo of the emitted method which can overide the provided method. public abstract MethodInfo GetHandlerForMethod(MethodBase method); } /// /// Provides information about a method emitter, including the ability to find if that /// particular method emitter can emit a method overides another method. /// public abstract class CMethodEmitterInfo { /// /// Returns true if this emitter can emit a method which overides the provided method. /// /// The method to check overrideability of. /// True, if this emitter can emit a method which overides the provided method, otherwise, false. public abstract bool GetEmittsMethod(MethodBase method); /// /// Emitts a method which can overide the provided method. /// /// The method to overide. /// The MethodInfo of the emitted method which can override the provided method. public abstract MethodInfo EmitMethod(MethodBase method); } /// /// CEmittedMethodInfo for Multidimensional Arrays /// 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!"); } } } /// /// CMethodEmitterInfo for Multidimensional Arrays /// 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); } } /// /// Operating instance of the DynamicMethodEmit class. /// public static DynamicMethodEmit Instance = new DynamicMethodEmit(AppDomain.CurrentDomain); /// /// AppDomain into which emission is occuring. /// public readonly AppDomain EmitDomain; /// /// Assembly into which emission is occuring. /// public readonly AssemblyBuilder EmitAssm; /// /// Module into which emission is occuring. /// public readonly ModuleBuilder EmitModu; /// /// Container Type of the methods which are currently being emitted. /// public TypeBuilder EmitContType; /// /// Number of Emitted menthods in the current Container Type. /// private int EmitCount = 0; /// /// Current number of Container Types. /// private int TypeCount = 0; /// /// List of all the currently emitted methods. /// /// /// If a CEmittedMethodInfo is to be used, it must be present in this list. /// List CEMIs = new List(); /// /// List of all the method emitters. /// /// /// If a CMethodEmitterInfo is to be used, it must be present in this list. /// List CMEIs = new List(); 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; } /// /// Begins a type to which emission will occur. /// After all desired methods have been emitted, /// call EndType() to get the MethodInfos. /// 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; } /// /// Finializes emission of a type. /// /// MethodInfos of the emitted methods. 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; } /// /// Returns true if DynamicMethodEmit Instance currently contains an emitter which can overide the provided Method. /// The method to search for an overide emitter for. /// True if a suitable emitter was found, otherwise, false. public static bool GetHasDynamicMethod(MethodBase method) { foreach (CMethodEmitterInfo cmei in Instance.CMEIs) { if (cmei.GetEmittsMethod(method)) return true; } return false; } /// /// Returns the MethodInfo of the method which can overide the provided method, /// generating it if nessecary. /// /// The method to overide. /// The MethodInfo of the suitable override. /// /// Thrown if a sutiable override could not be found or generated. /// 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(); } } }