// this file supports the VERBOSE_DEBUG define. this makes it emit a bunch of comments in the assembler output. // note that the tests are supposed to NOT include these comments #define VERBOSE_DEBUG using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using Indy.IL2CPU.Assembler; using Indy.IL2CPU.Assembler.X86; using Indy.IL2CPU.IL; using Mono.Cecil; using Mono.Cecil.Cil; using Instruction = Mono.Cecil.Cil.Instruction; namespace Indy.IL2CPU { public class MethodDefinitionComparer: IComparer { #region IComparer Members public int Compare(MethodDefinition x, MethodDefinition y) { return x.GetFullName().CompareTo(y.GetFullName()); } #endregion } public class FieldDefinitionComparer: IComparer { #region IComparer Members public int Compare(FieldDefinition x, FieldDefinition y) { return x.GetFullName().CompareTo(y.GetFullName()); } #endregion } public class TypeDefinitionComparer: IComparer { public int Compare(TypeDefinition x, TypeDefinition y) { return x.FullName.CompareTo(y.FullName); } } public class TypeDefinitionEqualityComparer: IEqualityComparer { public bool Equals(TypeDefinition x, TypeDefinition y) { return x.FullName.Equals(y.FullName); } public int GetHashCode(TypeDefinition obj) { return obj.FullName.GetHashCode(); } } public enum LogSeverityEnum { Informational, Warning } public delegate void DebugLogHandler(LogSeverityEnum aSeverity, string aMessage); public enum TargetPlatformEnum { x86 } public class Engine { protected static Engine mCurrent; protected AssemblyDefinition mCrawledAssembly; protected DebugLogHandler mDebugLog; protected OpCodeMap mMap; protected Assembler.Assembler mAssembler; /// /// Contains a list of all methods. This includes methods to be processed and already processed. /// protected SortedList mMethods = new SortedList(new MethodDefinitionComparer()); /// /// Contains a list of all static fields. This includes static fields to be processed and already processed. /// protected SortedList mStaticFields = new SortedList(new FieldDefinitionComparer()); /// /// Contains a list of custom method implementations. This is mainly used for iCalls /// protected SortedList mCustomMethodImplementation = new SortedList(); protected List mTypes = new List(); protected TypeDefinitionEqualityComparer mTypesEqualityComparer = new TypeDefinitionEqualityComparer(); /// /// 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. /// /// For now, only entrypoints without params and return code are supported! /// The assembly of which to crawl the entry-point method. /// The platform to target when assembling the code. /// public void Execute(string aAssembly, TargetPlatformEnum aTargetPlatform, StreamWriter aOutput) { mCurrent = this; try { if (aOutput == null) { throw new ArgumentNullException("aOutput"); } mCrawledAssembly = AssemblyFactory.GetAssembly(aAssembly); if (mCrawledAssembly.EntryPoint == null) { throw new NotSupportedException("Libraries are not supported!"); } using (mAssembler = new Assembler.X86.Assembler(aOutput)) { switch (aTargetPlatform) { case TargetPlatformEnum.x86: { mMap = (OpCodeMap)Activator.CreateInstance(Type.GetType("Indy.IL2CPU.IL.X86.X86OpCodeMap, Indy.IL2CPU.IL.X86", true)); break; } default: throw new NotSupportedException("TargetPlatform '" + aTargetPlatform + "' not supported!"); } mAssembler.OutputType = Indy.IL2CPU.Assembler.Assembler.OutputTypeEnum.Console; mMap.Initialize(mAssembler); foreach (Type t in typeof(Engine).Assembly.GetTypes()) { foreach (MethodInfo mi in t.GetMethods()) { object[] xAttribs = mi.GetCustomAttributes(typeof(MethodAliasAttribute), true); if (xAttribs != null && xAttribs.Length > 0) { MethodAliasAttribute xMethodAlias = (MethodAliasAttribute)xAttribs[0]; mCustomMethodImplementation.Add(xMethodAlias.Name, mi); } } } IL.Op.QueueMethod += QueueMethod; IL.Op.QueueStaticField += QueueStaticField; try { mTypes.Add(GetTypeDefinition("mscorlib", "System.Object")); mMethods.Add(RuntimeEngineRefs.InitializeApplicationRef, false); mMethods.Add(RuntimeEngineRefs.FinalizeApplicationRef, false); mMethods.Add(mCrawledAssembly.EntryPoint, false); // initialize the runtime engine mAssembler.Add( new Assembler.X86.Call("____INIT__VMT____"), new Assembler.X86.Call(new Label(RuntimeEngineRefs.InitializeApplicationRef).Name), new Assembler.X86.Call(new Label(mCrawledAssembly.EntryPoint).Name)); if (mCrawledAssembly.EntryPoint.ReturnType.ReturnType.FullName.StartsWith("System.Void", StringComparison.InvariantCultureIgnoreCase)) { mAssembler.Add(new Pushd("0")); } else { mAssembler.Add(new Pushd("eax")); } mAssembler.Add(new Assembler.X86.Call(new Label(RuntimeEngineRefs.FinalizeApplicationRef).Name)); ProcessAllMethods(); do { int xOldCount = mMethods.Count; ScanForMethodToIncludeForVMT(); ProcessAllMethods(); if (xOldCount == mMethods.Count) { break; } } while (true); GenerateVMT(); ProcessAllStaticFields(); } finally { mAssembler.Flush(); IL.Op.QueueMethod -= QueueMethod; IL.Op.QueueStaticField -= QueueStaticField; } } } finally { mCurrent = null; } } private void GenerateVMT() { // todo: abstract this code generation out to IL.* implementation mAssembler.Add(new Label("____INIT__VMT____")); mAssembler.Add(new Pushd("0" + mTypes.Count.ToString("X") + "h")); mAssembler.Add(new Call(new Label(VTablesImplRefs.LoadTypeTableRef).Name)); for (int i = 0; i < mTypes.Count; i++) { TypeDefinition xType = mTypes[i]; List xEmittedMethods = new List(); foreach (MethodDefinition xMethod in xType.Methods) { if (mMethods.ContainsKey(xMethod)) { xEmittedMethods.Add(xMethod); } } foreach (MethodDefinition xCtor in xType.Constructors) { if (mMethods.ContainsKey(xCtor)) { xEmittedMethods.Add(xCtor); } } mAssembler.Add(new Pushd("0" + i.ToString("X") + "h")); int? xBaseIndex = null; if (xType.BaseType == null) { for (int t = 0; t < mTypes.Count; t++) { if (mTypes[t].BaseType == null && mTypes[t].FullName == xType.FullName) { xBaseIndex = t; break; } } } else { for (int t = 0; t < mTypes.Count; t++) { if (mTypes[t].BaseType == null) { continue; } if (mTypes[t].BaseType.FullName == xType.BaseType.FullName && mTypes[t].FullName == xType.FullName) { xBaseIndex = t; break; } } } if (xBaseIndex == null) { throw new Exception("Base type not found!"); } mAssembler.Add(new Pushd("0" + xBaseIndex.Value.ToString("X") + "h")); mAssembler.Add(new Pushd("0" + xEmittedMethods.Count.ToString("X") + "h")); mAssembler.Add(new Call(new Label(VTablesImplRefs.SetTypeInfoRef).Name)); for (int j = 0; j < xEmittedMethods.Count; j++) { MethodDefinition xMethod = xEmittedMethods[j]; mAssembler.Add(new Pushd("0" + i.ToString("X") + "h")); mAssembler.Add(new Pushd("0" + j.ToString("X") + "h")); mAssembler.Add(new Pushd("0" + mMethods.IndexOfKey(mMethods.Keys.First(x => x.GetFullName() == xMethod.GetFullName())).ToString("X") + "h")); mAssembler.Add(new Pushd(new Label(xMethod).Name)); mAssembler.Add(new Call(new Label(VTablesImplRefs.SetMethodInfoRef).Name)); } } } private void ScanForMethodToIncludeForVMT() { List xCheckedTypes = new List(); foreach (MethodDefinition xMethod in mMethods.Keys) { if (xMethod.IsStatic) { continue; } TypeDefinition xCurrentType = GetDefinitionFromTypeReference(xMethod.DeclaringType); if (!xCheckedTypes.Contains(xCurrentType, mTypesEqualityComparer)) { xCheckedTypes.Add(xCurrentType); } } foreach (TypeDefinition xType in mTypes) { if (!xCheckedTypes.Contains(xType, mTypesEqualityComparer)) { xCheckedTypes.Add(xType); } } for (int i = 0; i < xCheckedTypes.Count; i++) { TypeDefinition 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 = GetDefinitionFromTypeReference(xCurrentType.BaseType); } } foreach (TypeDefinition xTD in xCheckedTypes) { foreach (MethodDefinition xMethod in xTD.Methods) { if (!xMethod.IsStatic) { if (xTD.BaseType == null) { continue; } if (xMethod.IsVirtual) { TypeDefinition xCurrentInspectedType = GetDefinitionFromTypeReference(xTD.BaseType); TypeReference[] xMethodParams = new TypeReference[xMethod.Parameters.Count]; for (int i = 0; i < xMethod.Parameters.Count; i++) { xMethodParams[i] = xMethod.Parameters[i].ParameterType; } MethodDefinition xBaseMethod = null; do { MethodDefinition xFoundMethod = xCurrentInspectedType.Methods.GetMethod(xMethod.Name, xMethodParams); if (xFoundMethod != null) { if (xFoundMethod.IsVirtual == xMethod.IsVirtual && xFoundMethod.IsPrivate == false && xFoundMethod.IsPublic == xMethod.IsPublic && xFoundMethod.IsFamily == xMethod.IsFamily && xFoundMethod.IsFamilyAndAssembly == xMethod.IsFamilyAndAssembly && xFoundMethod.IsFamilyOrAssembly == xMethod.IsFamilyOrAssembly && xFoundMethod.IsFinal == false) { xBaseMethod = xFoundMethod; } } if (xCurrentInspectedType.BaseType == null) { break; } xCurrentInspectedType = GetDefinitionFromTypeReference(xCurrentInspectedType.BaseType); } while (true); if (xBaseMethod != null) { if (mMethods.ContainsKey(xBaseMethod)) { QueueMethod(xMethod); } } } } } } } public static MethodDefinition GetDefinitionFromMethodReference(MethodReference aRef) { TypeDefinition xTypeDef; bool xIsArray = false; if (aRef.DeclaringType.FullName.Contains("[]") || aRef.DeclaringType.FullName.Contains("[,]") || aRef.DeclaringType.FullName.Contains("[,,]")) { xTypeDef = GetTypeDefinition("mscorlib", "System.Array"); xIsArray = true; } else { xTypeDef = GetDefinitionFromTypeReference(aRef.DeclaringType); } MethodDefinition xMethod = null; if (xIsArray) { if (aRef.Name == "Get") { xMethod = xTypeDef.Methods.GetMethod("GetValue", aRef.Parameters); } if (aRef.Name == "Set") { xMethod = xTypeDef.Methods.GetMethod("SetValue", aRef.Parameters); } } if (xMethod == null) { xMethod = xTypeDef.Methods.GetMethod(aRef.Name, aRef.Parameters); } if (xMethod != null) { return xMethod; } xMethod = xTypeDef.Constructors.GetConstructor(aRef.Name == MethodDefinition.Cctor, aRef.Parameters); if (xMethod != null) { return xMethod; } throw new Exception("Couldn't find Method! ('" + aRef.GetFullName() + "'"); } public static FieldDefinition GetDefinitionFromFieldReference(FieldReference aRef) { if (aRef == null) { throw new ArgumentNullException("aRef"); } if (mCurrent == null) { throw new Exception("No Current engine found!"); } FieldDefinition xTempResult = aRef as FieldDefinition; if (xTempResult != null) { return xTempResult; } TypeDefinition xTypeDef = GetDefinitionFromTypeReference(aRef.DeclaringType); FieldDefinition xResult = xTypeDef.Fields.GetField(aRef.Name); if (xResult == null) { throw new Exception("Field not Found! (" + aRef.Name + ")"); } return xResult; } public static TypeDefinition GetDefinitionFromTypeReference(TypeReference aRef) { if (aRef == null) { throw new ArgumentNullException("aRef"); } if (mCurrent == null) { throw new Exception("No Current engine found!"); } TypeDefinition xTempResult = aRef as TypeDefinition; if (xTempResult != null) { return xTempResult; } GenericParameter xGenParam = aRef as GenericParameter; if (xGenParam != null) { // todo: add support for generics return GetTypeDefinition("mscorlib", "System.Object"); } TypeSpecification xTypeSpec = aRef as TypeSpecification; if (xTypeSpec != null) { return GetDefinitionFromTypeReference(xTypeSpec.ElementType); } if (aRef.FullName.Contains("modreq")) { aRef = aRef.GetOriginalType(); } AssemblyNameReference xAssemblyNameReference = aRef.Scope as AssemblyNameReference; if (xAssemblyNameReference != null) { AssemblyDefinition xReferencedFieldAssembly; if (xAssemblyNameReference.FullName == typeof(RuntimeEngine).Assembly.GetName().FullName) { xReferencedFieldAssembly = RuntimeEngineRefs.RuntimeAssemblyDef; } else { xReferencedFieldAssembly = mCurrent.mCrawledAssembly.Resolver.Resolve(xAssemblyNameReference); } if (xReferencedFieldAssembly != null) { foreach (ModuleDefinition xModule in xReferencedFieldAssembly.Modules) { var xReferencedType = xModule.Types[aRef.FullName]; if (xReferencedType != null) { return xReferencedType; } { string theName = aRef.FullName; while (theName.EndsWith("[]")) { theName = theName.Substring(0, theName.Length - 2); } xReferencedType = xModule.Types[theName]; if (xReferencedType != null) { return xReferencedType; } } { string theName = aRef.FullName; while (theName.EndsWith("*")) { theName = theName.Substring(0, theName.Length - 1); } xReferencedType = xModule.Types[theName]; if (xReferencedType != null) { return xReferencedType; } } { string theName = aRef.FullName; while (theName.EndsWith("&")) { theName = theName.Substring(0, theName.Length - 1); } xReferencedType = xModule.Types[theName]; if (xReferencedType != null) { return xReferencedType; } } { string theName = aRef.FullName; if (theName.Contains("<") && theName.Contains(">")) { theName = theName.Substring(0, theName.IndexOf("<")); } xReferencedType = xModule.Types[theName]; if (xReferencedType != null) { return xReferencedType; } } } } } else { ModuleDefinition xReferencedModule = aRef.Scope as ModuleDefinition; if (xReferencedModule != null) { var xReferencedType = xReferencedModule.Types[aRef.FullName]; if (xReferencedType != null) { return xReferencedType; } { string theName = aRef.FullName; while (theName.EndsWith("[]")) { theName = theName.Substring(0, theName.Length - 2); } xReferencedType = xReferencedModule.Types[theName]; if (xReferencedType != null) { return xReferencedType; } } { string theName = aRef.FullName; while (theName.EndsWith("*")) { theName = theName.Substring(0, theName.Length - 1); } xReferencedType = xReferencedModule.Types[theName]; if (xReferencedType != null) { return xReferencedType; } } { string theName = aRef.FullName; while (theName.EndsWith("&")) { theName = theName.Substring(0, theName.Length - 1); } xReferencedType = xReferencedModule.Types[theName]; if (xReferencedType != null) { return xReferencedType; } } { string theName = aRef.FullName; if (theName.Contains("<") && theName.Contains(">")) { theName = theName.Substring(0, theName.IndexOf("<")); } xReferencedType = xReferencedModule.Types[theName]; if (xReferencedType != null) { return xReferencedType; } } } else { try { string theScopeText = aRef.Scope == null ? "**NULL**" : aRef.Scope.GetType().FullName; mCurrent.OnDebugLog(LogSeverityEnum.Informational, "Error: Unhandled scope: " + theScopeText); } catch (NullReferenceException) { System.Diagnostics.Debugger.Break(); string theScopeText = aRef.Scope == null ? "**NULL**" : aRef.Scope.GetType().FullName; mCurrent.OnDebugLog(LogSeverityEnum.Informational, "Error: Unhandled scope: " + theScopeText); } catch { throw; } } } string xModuleName = "**NOT DEFINED**"; if (aRef.Module != null && aRef.Module.Assembly != null) { xModuleName = aRef.Module.Assembly.Name.Name; } throw new Exception("Could not find TypeDefinition! (" + aRef.FullName + " in assembly " + xModuleName + ". TypeReference type = '" + aRef.GetType().FullName + "')"); } /// /// Gives the size to store an instance of the for use in a field. /// /// For classes, this is the pointer size. /// /// public static uint GetFieldStorageSize(TypeReference aType) { if (!aType.IsValueType) { return 4; } switch (aType.FullName) { case "System.Char": return 4; case "System.Byte": case "System.SByte": return 4; case "System.UInt16": case "System.Int16": return 4; 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 4; 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 } TypeDefinition xTypeDef = GetDefinitionFromTypeReference(aType); if (xTypeDef.IsEnum) { //System.Diagnostics.Debugger.Break(); return GetFieldStorageSize(xTypeDef.Fields.GetField("value__").FieldType); } uint xResult; GetTypeFieldInfo(xTypeDef, out xResult); return xResult; } private void ProcessAllStaticFields() { FieldDefinition xCurrentField; while ((xCurrentField = (from item in mStaticFields.Keys where !mStaticFields[item] select item).FirstOrDefault()) != null) { string xFieldName = xCurrentField.GetFullName(); OnDebugLog(LogSeverityEnum.Informational, "Processing Static Field '{0}', Constant = '{1}'({2})", xFieldName, xCurrentField.Constant, xCurrentField.Constant == null ? "**NULL**" : xCurrentField.Constant.GetType().FullName); xFieldName = DataMember.GetStaticFieldName(xCurrentField); RegisterType(GetDefinitionFromTypeReference(xCurrentField.FieldType)); if (xCurrentField.HasConstant) { // emit the constant, but first find out how we get it. System.Diagnostics.Debugger.Break(); } else { if (xCurrentField.InitialValue != null && xCurrentField.InitialValue.Length > 0) { string xTheData = ""; if (xCurrentField.InitialValue.Length > 4) { xTheData = "0,0,0,0,2,0,0,0,"; } foreach (byte x in BitConverter.GetBytes(xCurrentField.InitialValue.Length)) { xTheData += x + ","; } foreach (byte x in xCurrentField.InitialValue) { xTheData += x + ","; } xTheData = xTheData.TrimEnd(','); mAssembler.DataMembers.Add(new DataMember(xFieldName, "db", xTheData)); } else { uint xTheSize; string theType = "db"; if (xCurrentField.FieldType.IsValueType) { xTheSize = GetFieldStorageSize(xCurrentField.FieldType); } else { xTheSize = 4; } if (xTheSize == 4) { theType = "dd"; } string xTheData = ""; for (uint i = 0; i < xTheSize; i++) { xTheData += "0,"; } xTheData = xTheData.TrimEnd(','); mAssembler.DataMembers.Add(new DataMember(xFieldName, theType, xTheData)); } } mStaticFields[xCurrentField] = true; } } private void ProcessAllMethods() { MethodDefinition xCurrentMethod; while ((xCurrentMethod = (from item in mMethods.Keys where !mMethods[item] select item).FirstOrDefault()) != null) { OnDebugLog(LogSeverityEnum.Informational, "Processing method '{0}'", xCurrentMethod.GetFullName()); if (xCurrentMethod.IsAbstract) { mMethods[xCurrentMethod] = true; continue; } string xMethodName = new Label(xCurrentMethod).Name; foreach (CustomAttribute xAttrib in xCurrentMethod.CustomAttributes) { if (xAttrib.Constructor.DeclaringType.FullName == typeof(MethodAliasAttribute).FullName) { //xMethodName = (string)xAttrib.Fields["Name"]; break; } } TypeInformation xTypeInfo = null; { if (!xCurrentMethod.IsStatic) { uint xObjectStorageSize; SortedList xTypeFields = GetTypeFieldInfo(xCurrentMethod, out xObjectStorageSize); xTypeInfo = new TypeInformation(xObjectStorageSize, xTypeFields); } } MethodInformation xMethodInfo; { MethodInformation.Variable[] xVars = new MethodInformation.Variable[0]; int xCurOffset = 0; if (xCurrentMethod.HasBody) { xVars = new MethodInformation.Variable[xCurrentMethod.Body.Variables.Count]; foreach (VariableDefinition xVarDef in xCurrentMethod.Body.Variables) { int xVarSize = 4; xVars[xVarDef.Index] = new MethodInformation.Variable(xCurOffset, xVarSize); if (!(xVarDef.VariableType is GenericParameter)) { RegisterType(GetDefinitionFromTypeReference(xVarDef.VariableType)); } xCurOffset += xVarSize; } } MethodInformation.Argument[] xArgs; if (!xCurrentMethod.IsStatic) { xArgs = new MethodInformation.Argument[xCurrentMethod.Parameters.Count + 1]; xCurOffset = 0; int xArgSize; for (int i = xArgs.Length - 1; i > 0; i--) { xArgSize = 4; ParameterDefinition xParamDef = xCurrentMethod.Parameters[xArgs.Length - i - 1]; 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); xCurOffset += xArgSize; } xArgSize = 4; // this xArgs[0] = new MethodInformation.Argument(xArgSize, xCurOffset, MethodInformation.Argument.KindEnum.In); } else { xArgs = new MethodInformation.Argument[xCurrentMethod.Parameters.Count]; xCurOffset = 0; for (int i = xArgs.Length - 1; i >= 0; i--) { int xArgSize = 4; ParameterDefinition xParamDef = xCurrentMethod.Parameters[xArgs.Length - i - 1]; 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); xCurOffset += xArgSize; } } xMethodInfo = new MethodInformation(xMethodName, xVars, xArgs, !xCurrentMethod.ReturnType.ReturnType.FullName.Contains("System.Void"), !xCurrentMethod.IsStatic, xTypeInfo); } IL.Op xOp = GetOpFromType(mMap.MethodHeaderOp, null, xMethodInfo); xOp.Assembler = mAssembler; #if VERBOSE_DEBUG string comment = "Method: " + xCurrentMethod + "\r\n"; if (xCurrentMethod.Body == null) { comment += " (No locals)\r\n"; } else { comment += " Locals:\r\n"; foreach (VariableDefinition xVarDef in xCurrentMethod.Body.Variables) { comment += String.Format(" [{0}] {1}\r\n", xVarDef.Index, xVarDef.Name); } } comment += " Args:\r\n"; foreach (ParameterDefinition xParamDef in xCurrentMethod.Parameters) { comment += String.Format(" [{0}] {1}\r\n", xParamDef.Sequence, xParamDef.Name); } foreach (string s in comment.Trim().Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries)) { mAssembler.Add(new Literal(";" + s)); } #endif xOp.Assemble(); bool xIsCustomImplementation = false; MethodDefinition xCustomImplementation = null; if (mCustomMethodImplementation.ContainsKey(xCurrentMethod.GetFullName())) { xIsCustomImplementation = true; MethodInfo xReplacementMethod = mCustomMethodImplementation[xCurrentMethod.GetFullName()]; string[] xParamTypes = new string[xReplacementMethod.GetParameters().Length]; for (int i = 0; i < xReplacementMethod.GetParameters().Length; i++) { xParamTypes[i] = xReplacementMethod.GetParameters()[i].ParameterType.FullName; } xCustomImplementation = GetMethodDefinition(GetTypeDefinition(xReplacementMethod.DeclaringType.Assembly.FullName, xReplacementMethod.DeclaringType.FullName), xReplacementMethod.Name, xParamTypes); if (xCustomImplementation == null) { throw new Exception("CustomMethodImplementation not found!"); } } // 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) { if (Enum.GetNames(typeof(CustomMethodEnum)).Contains(xMethodName)) { CustomMethodImplementationOp xCustomMethodImplOp = (CustomMethodImplementationOp)GetOpFromType(mMap.CustomMethodImplementationOp, null, xMethodInfo); xCustomMethodImplOp.Assembler = mAssembler; xCustomMethodImplOp.Method = (CustomMethodEnum)Enum.Parse(typeof(CustomMethodEnum), xMethodName); xCustomMethodImplOp.Assemble(); } else { if (xCurrentMethod.HasBody) { // todo: add support for types which need different stack size foreach (Instruction xInstruction in xCurrentMethod.Body.Instructions) { MethodReference xMethodReference = xInstruction.Operand as MethodReference; if (xMethodReference != null) { QueueMethodRef(xMethodReference); } xOp = GetOpFromType(mMap.GetOpForOpCode(xInstruction.OpCode.Code), xInstruction, xMethodInfo); xOp.Assembler = mAssembler; xOp.Assemble(); } } else { if (xCurrentMethod.IsPInvokeImpl) { HandlePInvoke(xCurrentMethod, xMethodInfo); } else { OnDebugLog(LogSeverityEnum.Warning, "Method '{0}' not generated!", xCurrentMethod.GetFullName()); mAssembler.Add(new Literal("; Method not being generated yet, as it's handled by an iCall")); } } } } xOp = GetOpFromType(mMap.MethodFooterOp, null, xMethodInfo); xOp.Assembler = mAssembler; xOp.Assemble(); mMethods[xCurrentMethod] = true; } } public static SortedList GetTypeFieldInfo(MethodDefinition aCurrentMethod, out uint aObjectStorageSize) { TypeDefinition xCurrentInspectedType = GetDefinitionFromTypeReference(aCurrentMethod.DeclaringType); return GetTypeFieldInfo(xCurrentInspectedType, out aObjectStorageSize); } public static SortedList GetTypeFieldInfo(TypeDefinition aType, out uint aObjectStorageSize) { SortedList xTypeFields = new SortedList(); TypeDefinition xActualType = aType; aObjectStorageSize = 0; do { foreach (FieldDefinition xField in aType.Fields) { if (xField.IsStatic) { continue; } uint xFieldSize; TypeSpecification xTypeSpec = xField.FieldType as TypeSpecification; if (xTypeSpec != null) { xFieldSize = 4; RegisterTypeRef(xTypeSpec.ElementType); } else { TypeDefinition xFieldType = GetDefinitionFromTypeReference(xField.FieldType); if (xFieldType.IsClass) { xFieldSize = 4; } else { xFieldSize = GetFieldStorageSize(xField.FieldType); } } xTypeFields.Add(xField.ToString(), new TypeInformation.Field(aObjectStorageSize, xFieldSize)); aObjectStorageSize += xFieldSize; } if (aType.FullName != "System.Object" && aType.BaseType != null) { aType = GetDefinitionFromTypeReference(aType.BaseType); } else { break; } } while (true); if (xActualType.FullName == "System.String") { xTypeFields.Add("$$Storage$$", new TypeInformation.Field(aObjectStorageSize, 4)); aObjectStorageSize += 4; } return xTypeFields; } private static Op GetOpFromType(Type aType, Instruction aInstruction, MethodInformation aMethodInfo) { return (IL.Op)Activator.CreateInstance(aType, aInstruction, aMethodInfo); } public static void QueueStaticField(FieldDefinition aField) { if (mCurrent == null) { throw new Exception("ERROR: No Current Engine found!"); } if (!mCurrent.mStaticFields.ContainsKey(aField)) { mCurrent.mStaticFields.Add(aField, false); } } public static void QueueStaticField(string aAssembly, string aType, string aField, out string aFieldName) { if (mCurrent == null) { throw new Exception("ERROR: No Current Engine found!"); } TypeDefinition xTypeDef = GetTypeDefinition(aAssembly, aType); var xFieldDef = xTypeDef.Fields.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(FieldReference aFieldRef) { if (mCurrent == null) { throw new Exception("ERROR: No Current Engine found!"); } AssemblyNameReference xAssemblyNameReference = aFieldRef.DeclaringType.Scope as AssemblyNameReference; if (xAssemblyNameReference != null) { AssemblyDefinition xReferencedFieldAssembly; if (xAssemblyNameReference.FullName == typeof(RuntimeEngine).Assembly.GetName().FullName) { xReferencedFieldAssembly = RuntimeEngineRefs.RuntimeAssemblyDef; } else { xReferencedFieldAssembly = mCurrent.mCrawledAssembly.Resolver.Resolve(xAssemblyNameReference); } if (xReferencedFieldAssembly != null) { foreach (ModuleDefinition xModule in xReferencedFieldAssembly.Modules) { var xReferencedType = xModule.Types[aFieldRef.DeclaringType.FullName]; if (xReferencedType != null) { var xFieldDef = xReferencedType.Fields.GetField(aFieldRef.Name); if (xFieldDef != null) { QueueStaticField(xFieldDef); } break; } } } } else { ModuleDefinition xReferencedModule = aFieldRef.DeclaringType.Scope as ModuleDefinition; if (xReferencedModule != null) { var xReferencedType = xReferencedModule.Types[aFieldRef.DeclaringType.FullName]; if (xReferencedType != null) { var xFieldDef = xReferencedType.Fields.GetField(aFieldRef.Name); if (xFieldDef != null) { QueueStaticField(xFieldDef); } } } else { mCurrent.OnDebugLog(LogSeverityEnum.Informational, "Error: Unhandled scope: " + aFieldRef.DeclaringType.Scope == null ? "**NULL**" : aFieldRef.DeclaringType.Scope.GetType().FullName); } } } // 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(MethodDefinition aMethod) { if (mCurrent == null) { throw new Exception("ERROR: No Current Engine found!"); } if (!aMethod.IsStatic) { RegisterType(GetDefinitionFromTypeReference(aMethod.DeclaringType)); } if (!mCurrent.mMethods.ContainsKey(aMethod)) { mCurrent.mMethods.Add(aMethod, false); } } public static int GetMethodIdentifier(MethodDefinition aMethod) { QueueMethod(aMethod); return mCurrent.mMethods.IndexOfKey(aMethod); } public static int RegisterTypeRef(TypeReference aTypeRef) { if (aTypeRef == null) { throw new ArgumentNullException("aTypeRef"); } if (mCurrent == null) { throw new Exception("ERROR: No Current Engine found!"); } TypeSpecification xTypeSpec = aTypeRef as TypeSpecification; if (xTypeSpec != null) { return RegisterTypeRef(xTypeSpec.ElementType); } if (aTypeRef is GenericParameter) { return -1; } return RegisterType(GetDefinitionFromTypeReference(aTypeRef)); } /// /// Registers a type and returns the Type identifier /// /// /// public static int RegisterType(TypeDefinition aType) { if (aType == null) { throw new ArgumentNullException("aType"); } if (mCurrent == null) { throw new Exception("ERROR: No Current Engine found!"); } TypeDefinition 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) { TypeDefinition xCurInspectedType = GetDefinitionFromTypeReference(aType.BaseType); RegisterType(xCurInspectedType); } return RegisterType(aType); } else { return mCurrent.mTypes.IndexOf(xFoundItem); } } public static void QueueMethodRef(MethodReference aMethod) { if (mCurrent == null) { throw new Exception("ERROR: No Current Engine found!"); } QueueMethod(GetDefinitionFromMethodReference(aMethod)); } public static void QueueMethod2(string aAssembly, string aType, string aMethod) { MethodDefinition xMethodDef; QueueMethod2(aAssembly, aType, aMethod, out xMethodDef); } public static void QueueMethod2(string aAssembly, string aType, string aMethod, out MethodDefinition aMethodDef) { TypeDefinition xTypeDef = GetTypeDefinition(aAssembly, aType); // todo: find a way to specify one overload of a method int xCount = 0; aMethodDef = null; foreach (MethodDefinition xMethodDef in xTypeDef.Methods) { if (xMethodDef.Name == aMethod) { QueueMethod(xMethodDef); if (aMethodDef == null) { aMethodDef = xMethodDef; } xCount++; } } foreach (MethodDefinition xMethodDef in xTypeDef.Constructors) { 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)); } } public static TypeDefinition GetTypeDefinition(string aAssembly, string aType) { if (mCurrent == null) { throw new Exception("ERROR: No Current Engine found!"); } AssemblyDefinition xAssemblyDef; if (String.IsNullOrEmpty(aAssembly) || aAssembly == typeof(Engine).Assembly.GetName().Name || aAssembly == typeof(Engine).Assembly.GetName().FullName) { xAssemblyDef = AssemblyFactory.GetAssembly(typeof(Engine).Assembly.Location); } else { xAssemblyDef = mCurrent.mCrawledAssembly.Resolver.Resolve(aAssembly); } TypeDefinition xTypeDef = null; string xActualTypeName = aType; if (xActualTypeName.Contains("<") && xActualTypeName.Contains(">")) { xActualTypeName = xActualTypeName.Substring(0, xActualTypeName.IndexOf("<")); } foreach (ModuleDefinition xModDef in xAssemblyDef.Modules) { if (xModDef.Types.Contains(xActualTypeName)) { xTypeDef = xModDef.Types[xActualTypeName]; break; } } if (xTypeDef == null) { throw new Exception("Type '" + aType + "' not found in assembly '" + aAssembly + "'!"); } return xTypeDef; } public static MethodDefinition GetMethodDefinition(TypeDefinition aType, string aMethod, params string[] aParamTypes) { foreach (MethodDefinition xMethod in aType.Methods) { if (xMethod.Name != aMethod) { continue; } if (xMethod.Parameters.Count != aParamTypes.Length) { continue; } bool errorFound = false; for (int i = 0; i < xMethod.Parameters.Count; i++) { if (xMethod.Parameters[i].ParameterType.FullName != aParamTypes[i]) { errorFound = true; break; } } if (!errorFound) { return xMethod; } } foreach (MethodDefinition xMethod in aType.Constructors) { if (xMethod.Name != aMethod) { continue; } if (xMethod.Parameters.Count != aParamTypes.Length) { continue; } bool errorFound = false; for (int i = 0; i < xMethod.Parameters.Count; i++) { if (xMethod.Parameters[i].ParameterType.FullName != aParamTypes[i]) { errorFound = true; break; } } if (!errorFound) { return xMethod; } } throw new Exception("Method not found!"); } private void HandlePInvoke(MethodDefinition aMethod, MethodInformation aMethodInfo) { IL.Op xPInvokeMethodBodyOp = (IL.Op)Activator.CreateInstance(mMap.PInvokeMethodBodyOp, aMethod, aMethodInfo); xPInvokeMethodBodyOp.Assembler = mAssembler; xPInvokeMethodBodyOp.Assemble(); } } }