#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 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 { Win32, NativeX86 } public class QueuedMethodInformation { public bool Processed; public int Index; } 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. /// /// Whether or not the output is metalmode only. public void Execute(string aAssembly, TargetPlatformEnum aTargetPlatform, StreamWriter aOutput, bool aInMetalMode) { mCurrent = this; try { if (aOutput == null) { throw new ArgumentNullException("aOutput"); } mCrawledAssembly = AssemblyFactory.GetAssembly(aAssembly); ((DefaultAssemblyResolver)mCrawledAssembly.Resolver).AddSearchDirectory(Environment.CurrentDirectory); ((DefaultAssemblyResolver)mCrawledAssembly.Resolver).AddSearchDirectory(Path.GetDirectoryName(aAssembly)); if (mCrawledAssembly.EntryPoint == null) { throw new NotSupportedException("Libraries are not supported!"); } switch (aTargetPlatform) { case TargetPlatformEnum.Win32: { mMap = (OpCodeMap)Activator.CreateInstance(Type.GetType("Indy.IL2CPU.IL.X86.Win32.Win32OpCodeMap, Indy.IL2CPU.IL.X86.Win32", true)); mAssembler = new Assembler.X86.Win32.Assembler(aOutput, aInMetalMode); break; } case TargetPlatformEnum.NativeX86: { mMap = (OpCodeMap)Activator.CreateInstance(Type.GetType("Indy.IL2CPU.IL.X86.Native.NativeOpCodeMap, Indy.IL2CPU.IL.X86.Native", true)); mAssembler = new Assembler.X86.Native.Assembler(aOutput, aInMetalMode); break; } default: throw new NotSupportedException("TargetPlatform '" + aTargetPlatform + "' not supported!"); } using (mAssembler) { //mAssembler.OutputType = Assembler.Win32.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, new QueuedMethodInformation() { Processed = false, Index = mMethods.Count }); mMethods.Add(RuntimeEngineRefs.FinalizeApplicationRef, new QueuedMethodInformation() { Processed = false, Index = mMethods.Count }); if (!aInMetalMode) { 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.SetTypeInfoRef, new QueuedMethodInformation() { Processed = false, Index = mMethods.Count }); mMethods.Add(VTablesImplRefs.GetMethodAddressForTypeRef, new QueuedMethodInformation() { Processed = false, Index = mMethods.Count }); } mMethods.Add(mCrawledAssembly.EntryPoint, new QueuedMethodInformation() { Processed = false, Index = mMethods.Count }); // initialize the runtime engine MainEntryPointOp xEntryPointOp = (MainEntryPointOp)GetOpFromType(mMap.MainEntryPointOp, null, null); xEntryPointOp.Assembler = mAssembler; xEntryPointOp.Enter(Assembler.Assembler.EntryPointName); xEntryPointOp.Call(RuntimeEngineRefs.InitializeApplicationRef); if (!aInMetalMode) { xEntryPointOp.Call("____INIT__VMT____"); } xEntryPointOp.Call(mCrawledAssembly.EntryPoint); if (mCrawledAssembly.EntryPoint.ReturnType.ReturnType.FullName.StartsWith("System.Void", StringComparison.InvariantCultureIgnoreCase)) { xEntryPointOp.Pushd("0"); } xEntryPointOp.Call(RuntimeEngineRefs.FinalizeApplicationRef); xEntryPointOp.Exit(); ProcessAllMethods(); if (!aInMetalMode) { do { int xOldCount = mMethods.Count; ScanForMethodToIncludeForVMT(); ProcessAllMethods(); if (xOldCount == mMethods.Count) { break; } } while (true); GenerateVMT(); } mMap.PostProcess(mAssembler); // if(!aInMetalMode) { // FieldDefinition xFieldDef = VTablesImplRefs.VTablesImplDef.Fields.GetField("mIDTEntries"); // string xFieldName = Assembler.DataMember.GetStaticFieldName(xFieldDef); // string xFieldData = "0,0,0,0,2,0,0,0,1,0,0,0"; // for (int i = 0; i < 256; i++) { // xFieldData += ",0,0,0,0,0,0,0,0"; // } // mAssembler.DataMembers.RemoveAll(delegate(DataMember aItem) { // return aItem.Name == xFieldName; // }); // mAssembler.DataMembers.Add(new DataMember(xFieldName, "dd", xFieldName + "___Contents")); // mAssembler.DataMembers.Add(new DataMember(xFieldName + "___Contents", "db", xFieldData)); // } 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 Op xOp = GetOpFromType(mMap.MethodHeaderOp, null, new MethodInformation("____INIT__VMT____", new MethodInformation.Variable[0], new MethodInformation.Argument[0], 0, false, null, null)); 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.Methods = mMethods.Keys; xInitVmtOp.GetMethodIdentifier += delegate(MethodDefinition aMethod) { TypeReference[] xParams = new TypeReference[aMethod.Parameters.Count]; for (int i = 0; i < aMethod.Parameters.Count; i++) { xParams[i] = aMethod.Parameters[i].ParameterType; } MethodDefinition xMethod = GetUltimateBaseMethod(aMethod, xParams, GetDefinitionFromTypeReference(aMethod.DeclaringType)); return GetMethodIdentifier(xMethod); }; xInitVmtOp.Assemble(); xOp = GetOpFromType(mMap.MethodFooterOp, null, new MethodInformation("____INIT__VMT____", new MethodInformation.Variable[0], new MethodInformation.Argument[0], 0, false, null, null)); xOp.Assembler = mAssembler; xOp.Assemble(); } 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 = GetUltimateBaseMethod(xMethod, xMethodParams, xTD); if (xBaseMethod != null && xBaseMethod != xMethod) { if (mMethods.ContainsKey(xBaseMethod)) { QueueMethod(xMethod); } } } } } } } private static MethodDefinition GetUltimateBaseMethod(MethodDefinition aMethod, TypeReference[] aMethodParams, TypeDefinition aCurrentInspectedType) { MethodDefinition xBaseMethod = null; try { while (true) { if (aCurrentInspectedType.BaseType == null) { break; } aCurrentInspectedType = GetDefinitionFromTypeReference(aCurrentInspectedType.BaseType); MethodDefinition xFoundMethod = aCurrentInspectedType.Methods.GetMethod(aMethod.Name, aMethodParams); if (xFoundMethod != null) { 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) { xBaseMethod = xFoundMethod; } } } } catch (Exception E) { Console.WriteLine("Error while getting UltimateBaseMethod for method '{0}':\r\n{1}", aMethod.GetFullName(), E); // todo: try to get rid of the try..catch } return xBaseMethod ?? aMethod; } 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) { foreach (MethodDefinition xFoundMethod in xTypeDef.Methods) { if (xFoundMethod.Name != aRef.Name) { continue; } if (xFoundMethod.ReturnType.ReturnType.FullName != aRef.ReturnType.ReturnType.FullName) { continue; } if (xFoundMethod.Parameters.Count != aRef.Parameters.Count) { continue; } bool xMismatch = false; for (int i = 0; i < xFoundMethod.Parameters.Count; i++) { if (xFoundMethod.Parameters[i].ParameterType.FullName != aRef.Parameters[i].ParameterType.FullName) { xMismatch = true; break; } } if (!xMismatch) { xMethod = xFoundMethod; } } } 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; try { xReferencedFieldAssembly = mCurrent.mCrawledAssembly.Resolver.Resolve(xAssemblyNameReference); } catch { xReferencedFieldAssembly = null; } if (xReferencedFieldAssembly == null) { Assembly xAssembly = (from item in AppDomain.CurrentDomain.GetAssemblies() where item.GetName().Name == xAssemblyNameReference.Name select item).FirstOrDefault(); if (xAssembly != null) { xReferencedFieldAssembly = AssemblyFactory.GetAssembly(xAssembly.Location); } else { try { Assembly a = Assembly.Load(xAssemblyNameReference.Name); if (a != null) { xReferencedFieldAssembly = AssemblyFactory.GetAssembly(a.Location); } } catch { } } } 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) { 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 int GetFieldStorageSize(TypeReference aType) { if (aType.FullName == "System.Void") { return 0; } if (!aType.IsValueType) { return 4; } switch (aType.FullName) { case "System.Char": return 4; 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; } TypeDefinition xTypeDef = GetDefinitionFromTypeReference(aType); if (xTypeDef.IsEnum) { return GetFieldStorageSize(xTypeDef.Fields.GetField("value__").FieldType); } int 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); if (mAssembler.DataMembers.Count(x => x.Name == xFieldName) == 0) { RegisterType(GetDefinitionFromTypeReference(xCurrentField.FieldType)); if (xCurrentField.HasConstant) { // emit the constant, but first find out how we get it. } else { if (xCurrentField.InitialValue != null && xCurrentField.InitialValue.Length > 0) { string xTheData = ""; int xStorageSize = GetFieldStorageSize(xCurrentField.FieldType); 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(','); if (xTheData.Length == 0) { throw new Exception("Field '" + xCurrentField.ToString() + "' doesn't have a valid size!"); } mAssembler.DataMembers.Add(new DataMember(xFieldName, "db", xTheData)); } else { int xTheSize; string theType = "db"; TypeDefinition xFieldTypeDef = GetDefinitionFromTypeReference(xCurrentField.FieldType); TypeSpecification xTypeSpec = xCurrentField.FieldType as TypeSpecification; if (xTypeSpec == null) { if (!xFieldTypeDef.IsClass || xFieldTypeDef.IsValueType) { xTheSize = GetFieldStorageSize(xCurrentField.FieldType); } else { xTheSize = 4; } } else { xTheSize = 4; } if (xTheSize < 4) { // xTheSize = 4; } if (xTheSize == 4) { theType = "dd"; xTheSize = 1; } else { if (xTheSize == 2) { theType = "dw"; xTheSize = 1; } } string xTheData = ""; for (uint i = 0; i < xTheSize; i++) { xTheData += "0,"; } if (xTheSize == 0) { throw new Exception("Field '" + xCurrentField.ToString() + "' doesn't have a valid size!"); } 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].Processed select item).FirstOrDefault()) != null) { OnDebugLog(LogSeverityEnum.Informational, "Processing method '{0}'", xCurrentMethod.GetFullName()); if (xCurrentMethod.IsAbstract) { mMethods[xCurrentMethod].Processed = 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) { int xObjectStorageSize; SortedList xTypeFields = GetTypeFieldInfo(xCurrentMethod, out xObjectStorageSize); xTypeInfo = new TypeInformation(xObjectStorageSize, xTypeFields); } } MethodInformation xMethodInfo = GetMethodInfo(xCurrentMethod, xCurrentMethod, xMethodName, xTypeInfo); IL.Op xOp = GetOpFromType(mMap.MethodHeaderOp, null, xMethodInfo); xOp.Assembler = mAssembler; #if VERBOSE_DEBUG string comment = xMethodInfo.ToString(); 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!"); } } if (xCustomImplementation == null) { MethodReference xReplacement = mMap.GetCustomMethodImplementation(xMethodName, mAssembler.InMetalMode); if (xReplacement != null) { xCustomImplementation = GetDefinitionFromMethodReference(xReplacement); xIsCustomImplementation = true; } } // 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(xMethodName, mAssembler.InMetalMode)) { mMap.DoCustomAssembleImplementation(xMethodName, mAssembler.InMetalMode, mAssembler, xMethodInfo); } else { 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) { mAssembler.Add(new Literal("; StackItemCount = " + mAssembler.StackSizes.Count)); MethodReference xMethodReference = xInstruction.Operand as MethodReference; if (xMethodReference != null) { QueueMethodRef(xMethodReference); } xOp = GetOpFromType(mMap.GetOpForOpCode(xInstruction.OpCode.Code), xInstruction, xMethodInfo); if ((!xOp.SupportsMetalMode) && mAssembler.InMetalMode) { throw new Exception("OpCode '" + xInstruction.OpCode.Code + "' not supported in Metal mode!"); } 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(); mAssembler.StackSizes.Clear(); mMethods[xCurrentMethod].Processed = true; } } public static MethodInformation GetMethodInfo(MethodDefinition aCurrentMethodForArguments, MethodDefinition aCurrentMethodForLocals, string aMethodName, TypeInformation aTypeInfo) { MethodInformation xMethodInfo; { MethodInformation.Variable[] xVars = new MethodInformation.Variable[0]; int xCurOffset = 0; if (aCurrentMethodForLocals.HasBody) { xVars = new MethodInformation.Variable[aCurrentMethodForLocals.Body.Variables.Count]; foreach (VariableDefinition xVarDef in aCurrentMethodForLocals.Body.Variables) { int xVarSize = GetFieldStorageSize(xVarDef.VariableType); if ((xVarSize % 4) != 0) { xVarSize += 4 - (xVarSize % 4); } xVars[xVarDef.Index] = new MethodInformation.Variable(xCurOffset, xVarSize, GetDefinitionFromTypeReference(xVarDef.VariableType).IsClass); if (!(xVarDef.VariableType is GenericParameter)) { RegisterType(GetDefinitionFromTypeReference(xVarDef.VariableType)); } xCurOffset += xVarSize; } } MethodInformation.Argument[] xArgs; if (!aCurrentMethodForArguments.IsStatic) { xArgs = new MethodInformation.Argument[aCurrentMethodForArguments.Parameters.Count + 1]; xCurOffset = 0; int xArgSize; for (int i = xArgs.Length - 1; i > 0; i--) { ParameterDefinition xParamDef = aCurrentMethodForArguments.Parameters[xArgs.Length - 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); xCurOffset += xArgSize; } xArgSize = 4; // this xArgs[0] = new MethodInformation.Argument(xArgSize, xCurOffset, MethodInformation.Argument.KindEnum.In); } else { xArgs = new MethodInformation.Argument[aCurrentMethodForArguments.Parameters.Count]; xCurOffset = 0; for (int i = xArgs.Length - 1; i >= 0; i--) { ParameterDefinition xParamDef = aCurrentMethodForArguments.Parameters[xArgs.Length - i - 1]; int 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); xCurOffset += xArgSize; } } int xResultSize = GetFieldStorageSize(aCurrentMethodForArguments.ReturnType.ReturnType); xMethodInfo = new MethodInformation(aMethodName, xVars, xArgs, xResultSize, aCurrentMethodForArguments.HasThis, aTypeInfo, aCurrentMethodForArguments); } return xMethodInfo; } public static SortedList GetTypeFieldInfo(MethodDefinition aCurrentMethod, out int aObjectStorageSize) { TypeDefinition xCurrentInspectedType = GetDefinitionFromTypeReference(aCurrentMethod.DeclaringType); return GetTypeFieldInfo(xCurrentInspectedType, out aObjectStorageSize); } public static SortedList GetTypeFieldInfo(TypeDefinition aType, out int aObjectStorageSize) { SortedList xTypeFields = new SortedList(); TypeDefinition xActualType = aType; aObjectStorageSize = 0; do { foreach (FieldDefinition xField in aType.Fields) { if (xField.IsStatic) { continue; } if (xField.HasConstant) { Console.WriteLine("Field is constant: " + xField.GetFullName()); } int xFieldSize; TypeSpecification xTypeSpec = xField.FieldType as TypeSpecification; if (xTypeSpec != null) { xFieldSize = 4; RegisterTypeRef(xTypeSpec.ElementType); } else { TypeDefinition xFieldType = GetDefinitionFromTypeReference(xField.FieldType); if (xFieldType.IsClass && !xFieldType.IsValueType) { xFieldSize = 4; } else { xFieldSize = GetFieldStorageSize(xFieldType); } } 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, out string aDataName) { if (mCurrent == null) { throw new Exception("ERROR: No Current Engine found!"); } aDataName = ""; FieldDefinition xFieldDef = GetDefinitionFromFieldReference(aFieldRef); aDataName = DataMember.GetStaticFieldName(xFieldDef); QueueStaticField(xFieldDef); } // 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, new QueuedMethodInformation() { Processed = false, Index = mCurrent.mMethods.Count }); } } public static int GetMethodIdentifier(MethodDefinition aMethod) { QueueMethod(aMethod); return mCurrent.mMethods[aMethod].Index; } 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 AssemblyDefinition GetCrawledAssembly() { if (mCurrent == null) { throw new Exception("ERROR: No Current Engine found!"); } return mCurrent.mCrawledAssembly; } 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; 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) { xAssemblyDef = AssemblyFactory.GetAssembly(xAssembly.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(); } } }