using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using Cosmos.IL2CPU.Plugs; using System.Runtime.InteropServices; using Cosmos.Compiler.Assembler; namespace Cosmos.IL2CPU { public abstract class AppAssembler { protected ILOp[] mILOpsLo = new ILOp[256]; protected ILOp[] mILOpsHi = new ILOp[256]; private System.IO.TextWriter mLog; protected readonly Assembler mAssembler; public AppAssembler(Assembler assembler) { mAssembler = assembler; mLog = new System.IO.StreamWriter("Cosmos.Assembler.Log"); InitILOps(); } public Assembler Assembler { get { return mAssembler; } } protected virtual void MethodBegin(MethodInfo aMethod) { new Comment("---------------------------------------------------------"); new Comment("Type: " + aMethod.MethodBase.DeclaringType.ToString()); new Comment("Name: " + aMethod.MethodBase.Name); new Comment("Plugged: " + (aMethod.PlugMethod == null ? "No" : "Yes")); } protected virtual void MethodBegin(string aMethodName) { new Comment("---------------------------------------------------------"); new Comment("Name: " + aMethodName); } protected virtual void MethodEnd(string aMethodName) { new Comment("End Method: " + aMethodName); } protected virtual void MethodEnd(MethodInfo aMethod) { new Comment("End Method: " + aMethod.MethodBase.Name); } public void ProcessMethod(MethodInfo aMethod, List aOpCodes) { // We check this here and not scanner as when scanner makes these // plugs may still have not yet been scanned that it will depend on. // But by the time we make it here, they have to be resolved. if (aMethod.Type == MethodInfo.TypeEnum.NeedsPlug && aMethod.PlugMethod == null) { throw new Exception("Method needs plug, but no plug was assigned."); } // todo: MtW: how to do this? we need some extra space. // see ConstructLabel for extra info if (aMethod.UID > 0x00FFFFFF) { throw new Exception("For now, too much methods"); } MethodBegin(aMethod); mAssembler.Stack.Clear(); mLog.WriteLine("Method '{0}'", aMethod.MethodBase.GetFullName()); mLog.Flush(); if (aMethod.MethodAssembler != null) { mLog.WriteLine("Emitted using MethodAssembler", aMethod.MethodBase.GetFullName()); mLog.Flush(); var xAssembler = (AssemblerMethod)Activator.CreateInstance(aMethod.MethodAssembler); xAssembler.AssembleNew(mAssembler, aMethod.PluggedMethod); } else { foreach (var xOpCode in aOpCodes) { uint xOpCodeVal = (uint)xOpCode.OpCode; ILOp xILOp; if (xOpCodeVal <= 0xFF) { xILOp = mILOpsLo[xOpCodeVal]; } else { xILOp = mILOpsHi[xOpCodeVal & 0xFF]; } //mLog.WriteLine ( "\t[" + xILOp.ToString() + "] \t Stack start: " + Stack.Count.ToString() ); mLog.WriteLine("\t{0} {1}", mAssembler.Stack.Count, xILOp.GetType().Name); mLog.Flush(); BeforeOp(aMethod, xOpCode); new Comment("[" + xILOp.ToString() + "]"); var xNextPosition = xOpCode.Position + 1; #region Exception handling support code ExceptionHandlingClause xCurrentHandler = null; var xBody = aMethod.MethodBase.GetMethodBody(); // todo: add support for nested handlers using a stack or so.. foreach (ExceptionHandlingClause xHandler in xBody.ExceptionHandlingClauses) { if (xHandler.TryOffset > 0) { if (xHandler.TryOffset <= xNextPosition && (xHandler.TryLength + xHandler.TryOffset) > xNextPosition) { if (xCurrentHandler == null) { xCurrentHandler = xHandler; continue; } else if (xHandler.TryOffset > xCurrentHandler.TryOffset && (xHandler.TryLength + xHandler.TryOffset) < (xCurrentHandler.TryLength + xCurrentHandler.TryOffset)) { // only replace if the current found handler is narrower xCurrentHandler = xHandler; continue; } } } if (xHandler.HandlerOffset > 0) { if (xHandler.HandlerOffset <= xNextPosition && (xHandler.HandlerOffset + xHandler.HandlerLength) > xNextPosition) { if (xCurrentHandler == null) { xCurrentHandler = xHandler; continue; } else if (xHandler.HandlerOffset > xCurrentHandler.HandlerOffset && (xHandler.HandlerOffset + xHandler.HandlerLength) < (xCurrentHandler.HandlerOffset + xCurrentHandler.HandlerLength)) { // only replace if the current found handler is narrower xCurrentHandler = xHandler; continue; } } } if ((xHandler.Flags & ExceptionHandlingClauseOptions.Filter) > 0) { if (xHandler.FilterOffset > 0) { if (xHandler.FilterOffset <= xNextPosition) { if (xCurrentHandler == null) { xCurrentHandler = xHandler; continue; } else if (xHandler.FilterOffset > xCurrentHandler.FilterOffset) { // only replace if the current found handler is narrower xCurrentHandler = xHandler; continue; } } } } } #endregion var xNeedsExceptionPush = (xCurrentHandler != null) && (((xCurrentHandler.HandlerOffset > 0 && xCurrentHandler.HandlerOffset == xOpCode.Position) || ((xCurrentHandler.Flags & ExceptionHandlingClauseOptions.Filter) > 0 && xCurrentHandler.FilterOffset > 0 && xCurrentHandler.FilterOffset == xOpCode.Position)) && (xCurrentHandler.Flags == ExceptionHandlingClauseOptions.Clause)); if (xNeedsExceptionPush) { Push(0); mAssembler.Stack.Push(4, typeof(Exception)); } xILOp.Execute(aMethod, xOpCode); AfterOp(aMethod, xOpCode); //mLog.WriteLine( " end: " + Stack.Count.ToString() ); } } MethodEnd(aMethod); } // protected virtual void BeforeOp(MethodInfo aMethod, ILOpCode aOpCode) { } protected virtual void AfterOp(MethodInfo aMethod, ILOpCode aOpCode) { } protected abstract void InitILOps(); protected virtual void InitILOps(Type aAssemblerBaseOp) { foreach (var xType in aAssemblerBaseOp.Assembly.GetExportedTypes()) { if (xType.IsSubclassOf(aAssemblerBaseOp)) { var xAttribs = (OpCodeAttribute[])xType.GetCustomAttributes(typeof(OpCodeAttribute), false); foreach (var xAttrib in xAttribs) { var xOpCode = (ushort)xAttrib.OpCode; var xCtor = xType.GetConstructor(new Type[] { typeof(Assembler) }); var xILOp = (ILOp)xCtor.Invoke(new Object[] { Assembler }); if (xOpCode <= 0xFF) { mILOpsLo[xOpCode] = xILOp; } else { mILOpsHi[xOpCode & 0xFF] = xILOp; } } } } } protected abstract void Push(uint aValue); protected abstract void Push(string aLabelName); protected abstract void Call(MethodBase aMethod); protected abstract void Move(string aDestLabelName, int aValue); protected abstract void Jump(string aLabelName); protected abstract int GetVTableEntrySize(); public const string InitVMTCodeLabel = "___INIT__VMT__CODE____"; public void GenerateVMTCode(HashSet aTypesSet, HashSet aMethodsSet, Func aGetTypeID, Func aGetMethodUID) { // initialization MethodBegin(InitVMTCodeLabel); { var xSetTypeInfoRef = VTablesImplRefs.SetTypeInfoRef; var xSetMethodInfoRef = VTablesImplRefs.SetMethodInfoRef; var xLoadTypeTableRef = VTablesImplRefs.LoadTypeTableRef; var xTypesFieldRef = VTablesImplRefs.VTablesImplDef.GetField("mTypes", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); string xTheName = DataMember.GetStaticFieldName(xTypesFieldRef); DataMember xDataMember = (from item in Assembler.CurrentInstance.DataMembers where item.Name == xTheName select item).FirstOrDefault(); if (xDataMember != null) { Assembler.CurrentInstance.DataMembers.Remove((from item in Assembler.CurrentInstance.DataMembers where item == xDataMember select item).First()); } var xData = new byte[16 + (aTypesSet.Count * GetVTableEntrySize())]; var xTemp = BitConverter.GetBytes(aGetTypeID(typeof(Array))); xTemp = BitConverter.GetBytes(0x80000002); Array.Copy(xTemp, 0, xData, 4, 4); xTemp = BitConverter.GetBytes(aTypesSet.Count); Array.Copy(xTemp, 0, xData, 8, 4); xTemp = BitConverter.GetBytes(GetVTableEntrySize()); Array.Copy(xTemp, 0, xData, 12, 4); Assembler.CurrentInstance.DataMembers.Add(new DataMember(xTheName + "__Contents", xData)); Assembler.CurrentInstance.DataMembers.Add(new DataMember(xTheName, ElementReference.New(xTheName + "__Contents"))); #if VMT_DEBUG using (var xVmtDebugOutput = XmlWriter.Create(@"e:\vmt_debug.xml")) { xVmtDebugOutput.WriteStartDocument(); xVmtDebugOutput.WriteStartElement("VMT"); #endif //Push((uint)aTypesSet.Count); //Call(xLoadTypeTableRef); foreach (var xType in aTypesSet) { #if VMT_DEBUG xVmtDebugOutput.WriteStartElement("Type"); xVmtDebugOutput.WriteAttributeString("TypeId", aGetTypeID(xType).ToString()); if (xType.BaseType != null) { xVmtDebugOutput.WriteAttributeString("BaseTypeId", aGetTypeID(xType.BaseType).ToString()); } xVmtDebugOutput.WriteAttributeString("Name", xType.FullName); #endif // value contains true if the method is an interface method definition SortedList xEmittedMethods = new SortedList(new MethodBaseComparer()); foreach (MethodBase xMethod in xType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { if (aMethodsSet.Contains(xMethod))//) && !xMethod.IsAbstract) { xEmittedMethods.Add(xMethod, false); } } foreach (MethodBase xCtor in xType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { if (aMethodsSet.Contains(xCtor))// && !xCtor.IsAbstract) { xEmittedMethods.Add(xCtor, false); } } foreach (var xIntf in xType.GetInterfaces()) { foreach (var xMethodIntf in xIntf.GetMethods()) { var xActualMethod = xType.GetMethod(xIntf.FullName + "." + xMethodIntf.Name, (from xParam in xMethodIntf.GetParameters() select xParam.ParameterType).ToArray()); if (xActualMethod == null) { // get private implemenation xActualMethod = xType.GetMethod(xMethodIntf.Name, (from xParam in xMethodIntf.GetParameters() select xParam.ParameterType).ToArray()); } if (xActualMethod == null) { try { var xMap = xType.GetInterfaceMap(xIntf); for (int k = 0; k < xMap.InterfaceMethods.Length; k++) { if (xMap.InterfaceMethods[k] == xMethodIntf) { xActualMethod = xMap.TargetMethods[k]; break; } } } catch { } } if (aMethodsSet.Contains(xMethodIntf)) { if (!xEmittedMethods.ContainsKey(xMethodIntf)) { xEmittedMethods.Add(xMethodIntf, true); } } } } if (!xType.IsInterface) { Push(aGetTypeID(xType)); } int? xBaseIndex = null; if (xType.BaseType == null) { xBaseIndex = (int)aGetTypeID(xType); } else { for (int t = 0; t < aTypesSet.Count; t++) { // todo: optimize check var xItem = aTypesSet.Skip(t).First(); if (xItem.ToString() == xType.BaseType.ToString()) { xBaseIndex = (int)aGetTypeID(xItem); break; } } } if (xBaseIndex == null) { throw new Exception("Base type not found!"); } for (int x = xEmittedMethods.Count - 1; x >= 0; x--) { if (!aMethodsSet.Contains(xEmittedMethods.Keys[x])) { xEmittedMethods.RemoveAt(x); } } if (!xType.IsInterface) { Move("VMT__TYPE_ID_HOLDER__" + DataMember.FilterStringForIncorrectChars(MethodInfoLabelGenerator.GetFullName(xType) + " ASM_IS__" + xType.Assembly.GetName().Name), (int)aGetTypeID(xType)); Assembler.CurrentInstance.DataMembers.Add( new DataMember("VMT__TYPE_ID_HOLDER__" + DataMember.FilterStringForIncorrectChars(MethodInfoLabelGenerator.GetFullName(xType) + " ASM_IS__" + xType.Assembly.GetName().Name), new int[] { (int)aGetTypeID(xType) })); Push((uint)xBaseIndex.Value); xData = new byte[16 + (xEmittedMethods.Count * 4)]; xTemp = BitConverter.GetBytes(aGetTypeID(typeof(Array))); Array.Copy(xTemp, 0, xData, 0, 4); xTemp = BitConverter.GetBytes(0x80000002); // embedded array Array.Copy(xTemp, 0, xData, 4, 4); xTemp = BitConverter.GetBytes(xEmittedMethods.Count); // embedded array Array.Copy(xTemp, 0, xData, 8, 4); xTemp = BitConverter.GetBytes(4); // embedded array Array.Copy(xTemp, 0, xData, 12, 4); string xDataName = "____SYSTEM____TYPE___" + DataMember.FilterStringForIncorrectChars(MethodInfoLabelGenerator.GetFullName(xType) + " ASM_IS__" + xType.Assembly.GetName().Name) + "__MethodIndexesArray"; Assembler.CurrentInstance.DataMembers.Add(new DataMember(xDataName, xData)); Push(xDataName); xDataName = "____SYSTEM____TYPE___" + DataMember.FilterStringForIncorrectChars(MethodInfoLabelGenerator.GetFullName(xType) + " ASM_IS__" + xType.Assembly.GetName().Name) + "__MethodAddressesArray"; Assembler.CurrentInstance.DataMembers.Add(new DataMember(xDataName, xData)); Push(xDataName); xData = new byte[16 + Encoding.Unicode.GetByteCount(xType.FullName + ", " + xType.Module.Assembly.GetName().FullName)]; xTemp = BitConverter.GetBytes(aGetTypeID(typeof(Array))); Array.Copy(xTemp, 0, xData, 0, 4); xTemp = BitConverter.GetBytes(0x80000002); // embedded array Array.Copy(xTemp, 0, xData, 4, 4); xTemp = BitConverter.GetBytes((xType.FullName + ", " + xType.Module.Assembly.GetName().FullName).Length); Array.Copy(xTemp, 0, xData, 8, 4); xTemp = BitConverter.GetBytes(2); // embedded array Array.Copy(xTemp, 0, xData, 12, 4); xDataName = "____SYSTEM____TYPE___" + DataMember.FilterStringForIncorrectChars(MethodInfoLabelGenerator.GetFullName(xType) + " ASM_IS__" + xType.Assembly.GetName().Name); Assembler.CurrentInstance.DataMembers.Add(new DataMember(xDataName, xData)); Push("0" + xEmittedMethods.Count.ToString("X") + "h"); Call(xSetTypeInfoRef); } for (int j = 0; j < xEmittedMethods.Count; j++) { MethodBase xMethod = xEmittedMethods.Keys[j]; #if VMT_DEBUG xVmtDebugOutput.WriteStartElement("Method"); xVmtDebugOutput.WriteAttributeString("Id", aGetMethodUID(xMethod).ToString()); xVmtDebugOutput.WriteAttributeString("Name", xMethod.GetFullName()); xVmtDebugOutput.WriteEndElement(); #endif var xMethodId = aGetMethodUID(xMethod); if (!xType.IsInterface) { if (xEmittedMethods.Values[j]) { var xNewMethod = xType.GetMethod(xMethod.DeclaringType.FullName + "." + xMethod.Name, (from xParam in xMethod.GetParameters() select xParam.ParameterType).ToArray()); if (xNewMethod == null) { // get private implemenation xNewMethod = xType.GetMethod(xMethod.Name, (from xParam in xMethod.GetParameters() select xParam.ParameterType).ToArray()); } if (xNewMethod == null) { try { var xMap = xType.GetInterfaceMap(xMethod.DeclaringType); for (int k = 0; k < xMap.InterfaceMethods.Length; k++) { if (xMap.InterfaceMethods[k] == xMethod) { xNewMethod = xMap.TargetMethods[k]; break; } } } catch { } } xMethod = xNewMethod; } Push((uint)aGetTypeID(xType)); Push((uint)j); Push((uint)xMethodId); if (xMethod.IsAbstract) { // abstract methods dont have bodies, oiw, are not emitted Push(0); } else { Push(ILOp.GetMethodLabel(xMethod)); } Push(0); Call(VTablesImplRefs.SetMethodInfoRef); } } #if VMT_DEBUG xVmtDebugOutput.WriteEndElement(); // type #endif } #if VMT_DEBUG xVmtDebugOutput.WriteEndElement(); // types xVmtDebugOutput.WriteEndDocument(); } #endif } MethodEnd(InitVMTCodeLabel); } public void ProcessField(FieldInfo aField) { string xFieldName = MethodInfoLabelGenerator.GetFullName(aField); xFieldName = DataMember.GetStaticFieldName(aField); if (Assembler.CurrentInstance.DataMembers.Count(x => x.Name == xFieldName) == 0) { var xItemList = (from item in aField.GetCustomAttributes(false) where item.GetType().FullName == "ManifestResourceStreamAttribute" select item).ToList(); object xItem = null; if (xItemList.Count > 0) xItem = xItemList[0]; string xManifestResourceName = null; if (xItem != null) { var xItemType = xItem.GetType(); xManifestResourceName = (string)xItemType.GetField("ResourceName").GetValue(xItem); } if (xManifestResourceName != null) { // todo: add support for manifest streams again //RegisterType(xCurrentField.FieldType); //string xFileName = Path.Combine(mOutputDir, // (xCurrentField.DeclaringType.Assembly.FullName + "__" + xManifestResourceName).Replace(",", // "_") + ".res"); //using (var xStream = xCurrentField.DeclaringType.Assembly.GetManifestResourceStream(xManifestResourceName)) { // if (xStream == null) { // throw new Exception("Resource '" + xManifestResourceName + "' not found!"); // } // using (var xTarget = File.Create(xFileName)) { // // todo: abstract this array code out. // xTarget.Write(BitConverter.GetBytes(Engine.RegisterType(Engine.GetType("mscorlib", // "System.Array"))), // 0, // 4); // xTarget.Write(BitConverter.GetBytes((uint)InstanceTypeEnum.StaticEmbeddedArray), // 0, // 4); // xTarget.Write(BitConverter.GetBytes((int)xStream.Length), 0, 4); // xTarget.Write(BitConverter.GetBytes((int)1), 0, 4); // var xBuff = new byte[128]; // while (xStream.Position < xStream.Length) { // int xBytesRead = xStream.Read(xBuff, 0, 128); // xTarget.Write(xBuff, 0, xBytesRead); // } // } //} //mAssembler.DataMembers.Add(new DataMember("___" + xFieldName + "___Contents", // "incbin", // "\"" + xFileName + "\"")); //mAssembler.DataMembers.Add(new DataMember(xFieldName, // "dd", // "___" + xFieldName + "___Contents")); throw new NotImplementedException(); } else { uint xTheSize; //string theType = "db"; Type xFieldTypeDef = aField.FieldType; if (!xFieldTypeDef.IsClass || xFieldTypeDef.IsValueType) { xTheSize = GetSizeOfType(aField.FieldType); } else { xTheSize = 4; } byte[] xData = new byte[xTheSize]; try { object xValue = aField.GetValue(null); if (xValue != null) { try { xData = new byte[xTheSize]; if (xValue.GetType().IsValueType) { for (int x = 0; x < xTheSize; x++) { xData[x] = Marshal.ReadByte(xValue, x); } } } catch { } } } catch { } Assembler.CurrentInstance.DataMembers.Add(new DataMember(xFieldName, xData)); } } } public abstract uint GetSizeOfType(Type aType); public abstract void EmitEntrypoint(MethodBase aEntrypoint, IEnumerable aMethods); protected abstract void Ldarg(MethodInfo aMethod, int aIndex); protected abstract void Ldflda(MethodInfo aMethod, string aFieldId); protected abstract void Call(MethodInfo aMethod, MethodInfo aTargetMethod); protected abstract void Pop(); internal void GenerateMethodForward(MethodInfo aFrom, MethodInfo aTo) { // todo: completely get rid of this kind of trampoline code MethodBegin(aFrom); { var xParams = aTo.MethodBase.GetParameters().ToArray(); if (aTo.MethodAssembler != null) { xParams = aFrom.MethodBase.GetParameters(); } //if (aFrom.MethodBase.GetParameters().Length > 0 || !aFrom.MethodBase.IsStatic) { // Ldarg(aFrom, 0); // Pop(); //} int xCurParamIdx = 0; if (!aFrom.MethodBase.IsStatic) { Ldarg(aFrom, 0); xCurParamIdx++; if (aTo.MethodAssembler == null) { xParams = xParams.Skip(1).ToArray(); } } foreach (var xParam in xParams) { FieldAccessAttribute xFieldAccessAttrib = null; foreach (var xAttrib in xParam.GetCustomAttributes(typeof(FieldAccessAttribute), true)) { xFieldAccessAttrib = xAttrib as FieldAccessAttribute; } if (xFieldAccessAttrib != null) { // field access new Comment("Loading address of field '" + xFieldAccessAttrib.Name + "'"); Ldarg(aFrom, 0); Ldflda(aFrom, xFieldAccessAttrib.Name); } else { // normal field access new Comment("Loading parameter " + xCurParamIdx); Ldarg(aFrom, xCurParamIdx); xCurParamIdx++; } } Call(aFrom, aTo); } MethodEnd(aFrom); } } }