PageRenderTime 45ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/source2/IL2CPU/Cosmos.IL2CPU/ILOp.cs

https://bitbucket.org/mvptracker/cosmos
C# | 356 lines | 302 code | 31 blank | 23 comment | 61 complexity | 247d6268c2b2aeeffff4e5f77573f278 MD5 | raw file
Possible License(s): BSD-2-Clause
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Reflection;
  6. using Cosmos.IL2CPU.Plugs;
  7. using Cosmos.Assembler;
  8. using CPU = Cosmos.Assembler.x86;
  9. using Cosmos.IL2CPU.ILOpCodes;
  10. using Cosmos.Debug.Common;
  11. using Cosmos.IL2CPU.X86.IL;
  12. using System.Runtime.InteropServices;
  13. namespace Cosmos.IL2CPU {
  14. public abstract class ILOp {
  15. public static IDictionary<Type, IDictionary<string, PlugFieldAttribute>> mPlugFields;
  16. protected readonly Cosmos.Assembler.Assembler Assembler;
  17. protected ILOp(Cosmos.Assembler.Assembler aAsmblr) {
  18. Assembler = aAsmblr;
  19. }
  20. // This is called execute and not assemble, as the scanner
  21. // could be used for other things, profiling, analysis, reporting, etc
  22. public abstract void Execute(MethodInfo aMethod, ILOpCode aOpCode);
  23. public static string GetTypeIDLabel(Type aType) {
  24. return "VMT__TYPE_ID_HOLDER__" + DataMember.FilterStringForIncorrectChars(LabelName.GetFullName(aType) + " ASM_IS__" + aType.Assembly.GetName().Name);
  25. }
  26. public static uint Align(uint aSize, uint aAlign) {
  27. var xSize = aSize;
  28. if ((xSize % aAlign) != 0) {
  29. xSize += aAlign - (xSize % aAlign);
  30. }
  31. return xSize;
  32. }
  33. public static string GetLabel(MethodInfo aMethod, ILOpCode aOpCode) {
  34. return GetLabel(aMethod, aOpCode.Position);
  35. }
  36. public static string GetMethodLabel(MethodBase aMethod) {
  37. return LabelName.Get(aMethod);
  38. }
  39. public static string GetMethodLabel(MethodInfo aMethod) {
  40. if (aMethod.PluggedMethod != null) {
  41. return "PLUG_FOR___" + GetMethodLabel(aMethod.PluggedMethod.MethodBase);
  42. } else {
  43. return GetMethodLabel(aMethod.MethodBase);
  44. }
  45. }
  46. public static string GetLabel(MethodInfo aMethod, int aPos) {
  47. return LabelName.Get(GetMethodLabel(aMethod), aPos);
  48. }
  49. public override string ToString() {
  50. return GetType().Name;
  51. }
  52. protected static void Jump_Exception(MethodInfo aMethod) {
  53. // todo: port to numeric labels
  54. new CPU.Jump { DestinationLabel = GetMethodLabel(aMethod) + AppAssembler.EndOfMethodLabelNameException };
  55. }
  56. protected static void Jump_End(MethodInfo aMethod) {
  57. new CPU.Jump { DestinationLabel = GetMethodLabel(aMethod) + AppAssembler.EndOfMethodLabelNameNormal };
  58. }
  59. public static uint GetStackCountForLocal(MethodInfo aMethod, LocalVariableInfo aField) {
  60. var xSize = SizeOfType(aField.LocalType);
  61. var xResult = xSize / 4;
  62. if (xSize % 4 != 0) {
  63. xResult++;
  64. }
  65. return xResult;
  66. }
  67. public static uint GetEBPOffsetForLocal(MethodInfo aMethod, int localIndex) {
  68. var xBody = aMethod.MethodBase.GetMethodBody();
  69. uint xOffset = 4;
  70. for (int i = 0; i < xBody.LocalVariables.Count; i++) {
  71. if (i == localIndex) {
  72. break;
  73. }
  74. var xField = xBody.LocalVariables[i];
  75. xOffset += GetStackCountForLocal(aMethod, xField) * 4;
  76. }
  77. return xOffset;
  78. }
  79. public static uint SizeOfType(Type aType) {
  80. if (aType.FullName == "System.Void") {
  81. return 0;
  82. } else if ((!aType.IsValueType && aType.IsClass) || aType.IsInterface) {
  83. return 4;
  84. }
  85. if (aType.IsByRef) {
  86. return 4;
  87. }
  88. switch (aType.FullName) {
  89. case "System.Char":
  90. return 2;
  91. case "System.Byte":
  92. case "System.SByte":
  93. return 1;
  94. case "System.UInt16":
  95. case "System.Int16":
  96. return 2;
  97. case "System.UInt32":
  98. case "System.Int32":
  99. return 4;
  100. case "System.UInt64":
  101. case "System.Int64":
  102. return 8;
  103. //TODO: for now hardcode IntPtr and UIntPtr to be 32-bit
  104. case "System.UIntPtr":
  105. case "System.IntPtr":
  106. return 4;
  107. case "System.Boolean":
  108. return 1;
  109. case "System.Single":
  110. return 4;
  111. case "System.Double":
  112. return 8;
  113. case "System.Decimal":
  114. return 16;
  115. case "System.Guid":
  116. return 16;
  117. case "System.Enum":
  118. return 4;
  119. case "System.DateTime":
  120. return 8;
  121. }
  122. if (aType.FullName != null && aType.FullName.EndsWith("*")) {
  123. // pointer
  124. return 4;
  125. }
  126. // array
  127. //TypeSpecification xTypeSpec = aType as TypeSpecification;
  128. //if (xTypeSpec != null) {
  129. // return 4;
  130. //}
  131. if (aType.IsEnum) {
  132. return SizeOfType(aType.GetField("value__").FieldType);
  133. }
  134. if (aType.IsValueType) {
  135. var xSla = aType.StructLayoutAttribute;
  136. if (xSla != null) {
  137. if (xSla.Size > 0) {
  138. return (uint)xSla.Size;
  139. }
  140. }
  141. return (uint)(from item in GetFieldsInfo(aType)
  142. select (int)item.Size).Sum();
  143. }
  144. return 4;
  145. }
  146. public static uint GetEBPOffsetForLocalForDebugger(MethodInfo aMethod, int localIndex) {
  147. // because the memory is readed in positiv direction, we need to add additional size if greater than 4
  148. uint xOffset = GetEBPOffsetForLocal(aMethod, localIndex);
  149. var xBody = aMethod.MethodBase.GetMethodBody();
  150. var xField = xBody.LocalVariables[localIndex];
  151. xOffset += GetStackCountForLocal(aMethod, xField) * 4 - 4;
  152. return xOffset;
  153. }
  154. protected void ThrowNotImplementedException(string aMessage) {
  155. new CPU.Push {
  156. DestinationRef = Cosmos.Assembler.ElementReference.New(LdStr.GetContentsArrayName(aMessage))
  157. };
  158. new CPU.Call {
  159. DestinationLabel = LabelName.Get(typeof(ExceptionHelper).GetMethod("ThrowNotImplemented", BindingFlags.Static | BindingFlags.Public))
  160. };
  161. }
  162. protected void ThrowOverflowException() {
  163. new CPU.Call {
  164. DestinationLabel = LabelName.Get(
  165. typeof(ExceptionHelper).GetMethod("ThrowOverflow", BindingFlags.Static | BindingFlags.Public, null, new Type[] { }, null))
  166. };
  167. }
  168. private static void DoGetFieldsInfo(Type aType, List<X86.IL.FieldInfo> aFields) {
  169. var xCurList = new Dictionary<string, X86.IL.FieldInfo>();
  170. var xFields = (from item in aType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
  171. orderby item.Name, item.DeclaringType.ToString()
  172. select item).ToArray();
  173. for (int i = 0; i < xFields.Length; i++) {
  174. var xField = xFields[i];
  175. // todo: should be possible to have GetFields only return fields from a given type, thus removing need of next statement
  176. if (xField.DeclaringType != aType) {
  177. continue;
  178. }
  179. var xId = xField.GetFullName();
  180. var xInfo = new X86.IL.FieldInfo(xId, SizeOfType(xField.FieldType), aType, xField.FieldType);
  181. var xFieldOffsetAttrib = xField.GetCustomAttributes(typeof(FieldOffsetAttribute), true).FirstOrDefault() as FieldOffsetAttribute;
  182. if (xFieldOffsetAttrib != null) {
  183. xInfo.Offset = (uint)xFieldOffsetAttrib.Value;
  184. }
  185. aFields.Add(xInfo);
  186. xCurList.Add(xId, xInfo);
  187. }
  188. // now check plugs
  189. IDictionary<string, PlugFieldAttribute> xPlugFields;
  190. if (mPlugFields.TryGetValue(aType, out xPlugFields)) {
  191. foreach (var xPlugField in xPlugFields) {
  192. X86.IL.FieldInfo xPluggedField = null;
  193. if (xCurList.TryGetValue(xPlugField.Key, out xPluggedField)) {
  194. // plugfield modifies an already existing field
  195. // TODO: improve.
  196. if (xPlugField.Value.IsExternalValue) {
  197. xPluggedField.IsExternalValue = true;
  198. xPluggedField.FieldType = xPluggedField.FieldType.MakePointerType();
  199. xPluggedField.Size = 4;
  200. }
  201. } else {
  202. xPluggedField = new X86.IL.FieldInfo(xPlugField.Value.FieldId, SizeOfType(xPlugField.Value.FieldType), aType, xPlugField.Value.FieldType);
  203. aFields.Add(xPluggedField);
  204. }
  205. }
  206. }
  207. if (aType.BaseType != null) {
  208. DoGetFieldsInfo(aType.BaseType, aFields);
  209. }
  210. }
  211. protected static List<X86.IL.FieldInfo> GetFieldsInfo(Type aType) {
  212. var xResult = new List<X86.IL.FieldInfo>();
  213. DoGetFieldsInfo(aType, xResult);
  214. xResult.Reverse();
  215. uint xOffset = 0;
  216. foreach (var xInfo in xResult) {
  217. if (!xInfo.IsOffsetSet) {
  218. xInfo.Offset = xOffset;
  219. }
  220. xOffset += xInfo.Size;
  221. }
  222. var xDebugInfs = new List<FIELD_INFO>();
  223. foreach (var xInfo in xResult) {
  224. xDebugInfs.Add(new FIELD_INFO() {
  225. TYPE = xInfo.FieldType.AssemblyQualifiedName,
  226. OFFSET = (int)xInfo.Offset,
  227. NAME = GetNameForField(xInfo),
  228. });
  229. }
  230. DebugInfo.CurrentInstance.WriteFieldInfoToFile(xDebugInfs);
  231. List<DebugInfo.Field_Map> xFieldMapping = new List<DebugInfo.Field_Map>();
  232. GetFieldMapping(xResult, xFieldMapping, aType);
  233. DebugInfo.CurrentInstance.WriteFieldMappingToFile(xFieldMapping);
  234. return xResult;
  235. }
  236. private static void GetFieldMapping(List<X86.IL.FieldInfo> aFieldInfs, List<DebugInfo.Field_Map> aFieldMapping, Type aType) {
  237. DebugInfo.Field_Map xFMap = new DebugInfo.Field_Map();
  238. xFMap.TypeName = aType.AssemblyQualifiedName;
  239. foreach (var xInfo in aFieldInfs) {
  240. xFMap.FieldNames.Add(GetNameForField(xInfo));
  241. }
  242. aFieldMapping.Add(xFMap);
  243. }
  244. private static string GetNameForField(X86.IL.FieldInfo inf) {
  245. // First we need to separate out the
  246. // actual name of field from the type of the field.
  247. int loc = inf.Id.IndexOf(' ');
  248. if (loc >= 0) {
  249. string fName = inf.Id.Substring(loc, inf.Id.Length - loc);
  250. return inf.DeclaringType.AssemblyQualifiedName + fName;
  251. } else {
  252. return inf.Id;
  253. }
  254. }
  255. protected static uint GetStorageSize(Type aType) {
  256. return (from item in GetFieldsInfo(aType)
  257. orderby item.Offset descending
  258. select item.Offset + item.Size).FirstOrDefault();
  259. }
  260. public static void EmitExceptionLogic(Cosmos.Assembler.Assembler aAssembler, MethodInfo aMethodInfo, ILOpCode aCurrentOpCode, bool aDoTest, Action aCleanup) {
  261. EmitExceptionLogic(aAssembler, aMethodInfo, aCurrentOpCode, aDoTest, aCleanup, ILOp.GetLabel(aMethodInfo, aCurrentOpCode.NextPosition));
  262. }
  263. public static void EmitExceptionLogic(Cosmos.Assembler.Assembler aAssembler, MethodInfo aMethodInfo, ILOpCode aCurrentOpCode, bool aDoTest, Action aCleanup, string aJumpTargetNoException) {
  264. string xJumpTo = null;
  265. if (aCurrentOpCode != null && aCurrentOpCode.CurrentExceptionHandler != null) {
  266. // todo add support for nested handlers, see comment in Engine.cs
  267. //if (!((aMethodInfo.CurrentHandler.HandlerOffset < aCurrentOpOffset) || (aMethodInfo.CurrentHandler.HandlerLength + aMethodInfo.CurrentHandler.HandlerOffset) <= aCurrentOpOffset)) {
  268. new Comment(String.Format("CurrentOffset = {0}, HandlerStartOffset = {1}", aCurrentOpCode.Position, aCurrentOpCode.CurrentExceptionHandler.HandlerOffset));
  269. if (aCurrentOpCode.CurrentExceptionHandler.HandlerOffset > aCurrentOpCode.Position) {
  270. switch (aCurrentOpCode.CurrentExceptionHandler.Flags) {
  271. case ExceptionHandlingClauseOptions.Clause: {
  272. xJumpTo = ILOp.GetLabel(aMethodInfo, aCurrentOpCode.CurrentExceptionHandler.HandlerOffset);
  273. break;
  274. }
  275. case ExceptionHandlingClauseOptions.Finally: {
  276. xJumpTo = ILOp.GetLabel(aMethodInfo, aCurrentOpCode.CurrentExceptionHandler.HandlerOffset);
  277. break;
  278. }
  279. default: {
  280. throw new Exception("ExceptionHandlerType '" + aCurrentOpCode.CurrentExceptionHandler.Flags.ToString() + "' not supported yet!");
  281. }
  282. }
  283. }
  284. }
  285. // if aDoTest is true, we check ECX for exception flags
  286. if (!aDoTest) {
  287. //new CPU.Call("_CODE_REQUESTED_BREAK_");
  288. if (xJumpTo == null) {
  289. Jump_Exception(aMethodInfo);
  290. } else {
  291. new CPU.Jump { DestinationLabel = xJumpTo };
  292. }
  293. } else {
  294. new CPU.Test { DestinationReg = CPU.Registers.ECX, SourceValue = 2 };
  295. if (aCleanup != null) {
  296. new CPU.ConditionalJump { Condition = CPU.ConditionalTestEnum.Equal, DestinationLabel = aJumpTargetNoException };
  297. aCleanup();
  298. if (xJumpTo == null) {
  299. new CPU.ConditionalJump { Condition = CPU.ConditionalTestEnum.NotEqual, DestinationLabel = GetMethodLabel(aMethodInfo) + AppAssembler.EndOfMethodLabelNameException };
  300. } else {
  301. new CPU.ConditionalJump { Condition = CPU.ConditionalTestEnum.NotEqual, DestinationLabel = xJumpTo };
  302. }
  303. } else {
  304. if (xJumpTo == null) {
  305. new CPU.ConditionalJump { Condition = CPU.ConditionalTestEnum.NotEqual, DestinationLabel = GetMethodLabel(aMethodInfo) + AppAssembler.EndOfMethodLabelNameException };
  306. } else {
  307. new CPU.ConditionalJump { Condition = CPU.ConditionalTestEnum.NotEqual, DestinationLabel = xJumpTo };
  308. }
  309. }
  310. }
  311. }
  312. public static bool IsIntegerSigned(Type aType) {
  313. switch (aType.FullName) {
  314. case "System.SByte":
  315. case "System.Int16":
  316. case "System.Int32":
  317. case "System.Int64":
  318. //TODO not sure about this case "System.IntPtr":
  319. //TODO not sure aobut this case "System.Enum":
  320. return true;
  321. }
  322. return false;
  323. }
  324. }
  325. }