PageRenderTime 62ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/source2/IL2CPU/Cosmos.IL2CPU/AppAssembler.cs

https://bitbucket.org/mvptracker/cosmos
C# | 1061 lines | 868 code | 86 blank | 107 comment | 232 complexity | 2ff07b9b7ea0741e550b545855fc5fce MD5 | raw file
Possible License(s): BSD-2-Clause
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics.SymbolStore;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Runtime.InteropServices;
  8. using System.Text;
  9. using Cosmos.Assembler;
  10. using Cosmos.Assembler.x86;
  11. using Cosmos.Build.Common;
  12. using Cosmos.Debug.Common;
  13. using Cosmos.IL2CPU.Plugs;
  14. using Mono.Cecil;
  15. namespace Cosmos.IL2CPU {
  16. public class AppAssembler: IDisposable {
  17. public const string EndOfMethodLabelNameNormal = ".END__OF__METHOD_NORMAL";
  18. public const string EndOfMethodLabelNameException = ".END__OF__METHOD_EXCEPTION";
  19. protected const string InitStringIDsLabel = "___INIT__STRINGS_TYPE_ID_S___";
  20. protected List<LOCAL_ARGUMENT_INFO> mLocals_Arguments_Infos = new List<LOCAL_ARGUMENT_INFO>();
  21. protected ILOp[] mILOpsLo = new ILOp[256];
  22. protected ILOp[] mILOpsHi = new ILOp[256];
  23. public bool ShouldOptimize = false;
  24. public DebugInfo DebugInfo { get; set; }
  25. protected System.IO.TextWriter mLog;
  26. protected Dictionary<string, ModuleDefinition> mLoadedModules = new Dictionary<string, ModuleDefinition>();
  27. protected DebugInfo.SequencePoint[] mSequences = new DebugInfo.SequencePoint[0];
  28. public TraceAssemblies TraceAssemblies;
  29. public bool DebugEnabled = false;
  30. public DebugMode DebugMode;
  31. public bool IgnoreDebugStubAttribute;
  32. protected static HashSet<string> mDebugLines = new HashSet<string>();
  33. protected List<MethodIlOp> mSymbols = new List<MethodIlOp>();
  34. public readonly Cosmos.Assembler.Assembler Assembler;
  35. //
  36. protected string mCurrentMethodLabel;
  37. protected Guid mCurrentMethodLabelEndGuid;
  38. protected Guid mCurrentMethodGuid;
  39. public AppAssembler(int aComPort) {
  40. Assembler = new Cosmos.Assembler.Assembler(aComPort);
  41. mLog = new System.IO.StreamWriter("Cosmos.Assembler.Log", false);
  42. InitILOps();
  43. }
  44. public void Dispose()
  45. {
  46. if (mLog != null)
  47. {
  48. mLog.Dispose();
  49. mLog = null;
  50. }
  51. GC.SuppressFinalize(this);
  52. }
  53. protected void MethodBegin(MethodInfo aMethod) {
  54. new Comment("---------------------------------------------------------");
  55. new Comment("Assembly: " + aMethod.MethodBase.DeclaringType.Assembly.FullName);
  56. new Comment("Type: " + aMethod.MethodBase.DeclaringType.ToString());
  57. new Comment("Name: " + aMethod.MethodBase.Name);
  58. new Comment("Plugged: " + (aMethod.PlugMethod == null ? "No" : "Yes"));
  59. // Issue label that is used for calls etc.
  60. string xMethodLabel;
  61. if (aMethod.PluggedMethod != null) {
  62. xMethodLabel = "PLUG_FOR___" + LabelName.Get(aMethod.PluggedMethod.MethodBase);
  63. } else {
  64. xMethodLabel = LabelName.Get(aMethod.MethodBase);
  65. }
  66. new Cosmos.Assembler.Label(xMethodLabel);
  67. // We could use same GUID as MethodLabelStart, but its better to keep GUIDs unique globaly for items
  68. // so during debugging they can never be confused as to what they point to.
  69. mCurrentMethodGuid = Guid.NewGuid();
  70. // We issue a second label for GUID. This is increases label count, but for now we need a master label first.
  71. // We issue a GUID label to reduce amount of work and time needed to construct debugging DB.
  72. var xLabelGuid = Guid.NewGuid();
  73. new Cosmos.Assembler.Label("GUID_" + xLabelGuid.ToString("N"));
  74. mCurrentMethodLabel = "METHOD_" + xLabelGuid.ToString("N");
  75. Cosmos.Assembler.Label.LastFullLabel = mCurrentMethodLabel;
  76. mCurrentMethodLabelEndGuid = Guid.NewGuid();
  77. if (aMethod.MethodBase.IsStatic && aMethod.MethodBase is ConstructorInfo) {
  78. new Comment("Static constructor. See if it has been called already, return if so.");
  79. var xName = DataMember.FilterStringForIncorrectChars("CCTOR_CALLED__" + LabelName.GetFullName(aMethod.MethodBase.DeclaringType));
  80. var xAsmMember = new DataMember(xName, (byte)0);
  81. Assembler.DataMembers.Add(xAsmMember);
  82. new Compare { DestinationRef = Cosmos.Assembler.ElementReference.New(xName), DestinationIsIndirect = true, Size = 8, SourceValue = 1 };
  83. new ConditionalJump { Condition = ConditionalTestEnum.Equal, DestinationLabel = ".BeforeQuickReturn" };
  84. new Mov { DestinationRef = Cosmos.Assembler.ElementReference.New(xName), DestinationIsIndirect = true, Size = 8, SourceValue = 1 };
  85. new Jump { DestinationLabel = ".AfterCCTorAlreadyCalledCheck" };
  86. new Cosmos.Assembler.Label(".BeforeQuickReturn");
  87. new Mov { DestinationReg = RegistersEnum.ECX, SourceValue = 0 };
  88. new Return { };
  89. new Cosmos.Assembler.Label(".AfterCCTorAlreadyCalledCheck");
  90. }
  91. new Push { DestinationReg = Registers.EBP };
  92. new Mov { DestinationReg = Registers.EBP, SourceReg = Registers.ESP };
  93. if (DebugMode == DebugMode.Source) {
  94. // Would be nice to use xMethodSymbols.GetSourceStartEnd but we cant
  95. // because its not implemented by the unmanaged code underneath.
  96. //
  97. // This doesnt seem right to store as a field, but old code had it that way so we
  98. // continue using a field for now.
  99. mSequences = DebugInfo.GetSequencePoints(aMethod.MethodBase, true);
  100. if (mSequences.Length > 0) {
  101. DebugInfo.AddDocument(mSequences[0].Document);
  102. var xMethod = new Method() {
  103. ID = mCurrentMethodGuid,
  104. TypeToken = aMethod.MethodBase.DeclaringType.MetadataToken,
  105. MethodToken = aMethod.MethodBase.MetadataToken,
  106. LabelStartID = xLabelGuid,
  107. LabelEndID = mCurrentMethodLabelEndGuid,
  108. LabelCall = xMethodLabel,
  109. AssemblyFileID = DebugInfo.AssemblyGUIDs[aMethod.MethodBase.DeclaringType.Assembly],
  110. DocumentID = DebugInfo.DocumentGUIDs[mSequences[0].Document],
  111. // Storing Line + Col as one item makes comparisons MUCH easier, otherwise we have to
  112. // check for things like col < start col but line > start line.
  113. //
  114. // () around << are VERY important.. + has precedence over <<
  115. LineColStart = ((Int64)mSequences[0].LineStart << 32) + mSequences[0].ColStart,
  116. LineColEnd = ((Int64)(mSequences[mSequences.Length - 1].LineEnd) << 32) + mSequences[mSequences.Length - 1].ColEnd
  117. };
  118. DebugInfo.AddMethod(xMethod);
  119. }
  120. }
  121. if (aMethod.MethodAssembler == null && aMethod.PlugMethod == null && !aMethod.IsInlineAssembler) {
  122. // the body of aMethod is getting emitted
  123. var xBody = aMethod.MethodBase.GetMethodBody();
  124. if (xBody != null) {
  125. var xLocalsOffset = mLocals_Arguments_Infos.Count;
  126. foreach (var xLocal in xBody.LocalVariables) {
  127. var xInfo = new LOCAL_ARGUMENT_INFO {
  128. METHODLABELNAME = mCurrentMethodLabel,
  129. IsArgument = false,
  130. INDEXINMETHOD = xLocal.LocalIndex,
  131. NAME = "Local" + xLocal.LocalIndex,
  132. OFFSET = 0 - (int)ILOp.GetEBPOffsetForLocalForDebugger(aMethod, xLocal.LocalIndex),
  133. TYPENAME = xLocal.LocalType.AssemblyQualifiedName
  134. };
  135. mLocals_Arguments_Infos.Add(xInfo);
  136. var xSize = ILOp.Align(ILOp.SizeOfType(xLocal.LocalType), 4);
  137. new Comment(String.Format("Local {0}, Size {1}", xLocal.LocalIndex, xSize));
  138. for (int i = 0; i < xSize / 4; i++) {
  139. new Push { DestinationValue = 0 };
  140. }
  141. //new Sub { DestinationReg = Registers.ESP, SourceValue = ILOp.Align(ILOp.SizeOfType(xLocal.LocalType), 4) };
  142. }
  143. var xCecilMethod = GetCecilMethodDefinitionForSymbolReading(aMethod.MethodBase);
  144. if (xCecilMethod != null && xCecilMethod.Body != null) {
  145. // mLocals_Arguments_Infos is one huge list, so ourlatest additions are at the end
  146. for (int i = 0; i < xCecilMethod.Body.Variables.Count; i++) {
  147. mLocals_Arguments_Infos[xLocalsOffset + i].NAME = xCecilMethod.Body.Variables[i].Name;
  148. }
  149. for (int i = xLocalsOffset + xCecilMethod.Body.Variables.Count - 1; i >= xLocalsOffset; i--) {
  150. if (mLocals_Arguments_Infos[i].NAME.Contains('$')) {
  151. mLocals_Arguments_Infos.RemoveAt(i);
  152. }
  153. }
  154. }
  155. }
  156. // debug info:
  157. var xIdxOffset = 0u;
  158. if (!aMethod.MethodBase.IsStatic) {
  159. mLocals_Arguments_Infos.Add(new LOCAL_ARGUMENT_INFO {
  160. METHODLABELNAME = mCurrentMethodLabel,
  161. IsArgument = true,
  162. NAME = "this:" + X86.IL.Ldarg.GetArgumentDisplacement(aMethod, 0),
  163. INDEXINMETHOD = 0,
  164. OFFSET = X86.IL.Ldarg.GetArgumentDisplacement(aMethod, 0),
  165. TYPENAME = aMethod.MethodBase.DeclaringType.AssemblyQualifiedName
  166. });
  167. xIdxOffset++;
  168. }
  169. var xParams = aMethod.MethodBase.GetParameters();
  170. var xParamCount = (ushort)xParams.Length;
  171. for (ushort i = 0; i < xParamCount; i++) {
  172. var xOffset = X86.IL.Ldarg.GetArgumentDisplacement(aMethod, (ushort)(i + xIdxOffset));
  173. // if last argument is 8 byte long, we need to add 4, so that debugger could read all 8 bytes from this variable in positiv direction
  174. xOffset -= (int)Cosmos.IL2CPU.ILOp.Align(ILOp.SizeOfType(xParams[i].ParameterType), 4) - 4;
  175. mLocals_Arguments_Infos.Add(new LOCAL_ARGUMENT_INFO {
  176. METHODLABELNAME = mCurrentMethodLabel,
  177. IsArgument = true,
  178. INDEXINMETHOD = (int)(i + xIdxOffset),
  179. NAME = xParams[i].Name,
  180. OFFSET = xOffset,
  181. TYPENAME = xParams[i].ParameterType.AssemblyQualifiedName
  182. });
  183. }
  184. }
  185. }
  186. protected void MethodEnd(MethodInfo aMethod) {
  187. new Comment("End Method: " + aMethod.MethodBase.Name);
  188. uint xReturnSize = 0;
  189. var xMethInfo = aMethod.MethodBase as System.Reflection.MethodInfo;
  190. if (xMethInfo != null) {
  191. xReturnSize = ILOp.Align(ILOp.SizeOfType(xMethInfo.ReturnType), 4);
  192. }
  193. if (aMethod.PlugMethod == null && !aMethod.IsInlineAssembler) {
  194. new Cosmos.Assembler.Label(ILOp.GetMethodLabel(aMethod) + EndOfMethodLabelNameNormal);
  195. }
  196. new Mov { DestinationReg = Registers.ECX, SourceValue = 0 };
  197. var xTotalArgsSize = (from item in aMethod.MethodBase.GetParameters()
  198. select (int)ILOp.Align(ILOp.SizeOfType(item.ParameterType), 4)).Sum();
  199. if (!aMethod.MethodBase.IsStatic) {
  200. if (aMethod.MethodBase.DeclaringType.IsValueType) {
  201. xTotalArgsSize += 4; // only a reference is passed
  202. } else {
  203. xTotalArgsSize += (int)ILOp.Align(ILOp.SizeOfType(aMethod.MethodBase.DeclaringType), 4);
  204. }
  205. }
  206. if (aMethod.PluggedMethod != null) {
  207. xReturnSize = 0;
  208. xMethInfo = aMethod.PluggedMethod.MethodBase as System.Reflection.MethodInfo;
  209. if (xMethInfo != null) {
  210. xReturnSize = ILOp.Align(ILOp.SizeOfType(xMethInfo.ReturnType), 4);
  211. }
  212. xTotalArgsSize = (from item in aMethod.PluggedMethod.MethodBase.GetParameters()
  213. select (int)ILOp.Align(ILOp.SizeOfType(item.ParameterType), 4)).Sum();
  214. if (!aMethod.PluggedMethod.MethodBase.IsStatic) {
  215. if (aMethod.PluggedMethod.MethodBase.DeclaringType.IsValueType) {
  216. xTotalArgsSize += 4; // only a reference is passed
  217. } else {
  218. xTotalArgsSize += (int)ILOp.Align(ILOp.SizeOfType(aMethod.PluggedMethod.MethodBase.DeclaringType), 4);
  219. }
  220. }
  221. }
  222. if (xReturnSize > 0) {
  223. var xOffset = GetResultCodeOffset(xReturnSize, (uint)xTotalArgsSize);
  224. for (int i = 0; i < xReturnSize / 4; i++) {
  225. new Pop { DestinationReg = Registers.EAX };
  226. new Mov {
  227. DestinationReg = Registers.EBP,
  228. DestinationIsIndirect = true,
  229. DestinationDisplacement = (int)(xOffset + ((i + 0) * 4)),
  230. SourceReg = Registers.EAX
  231. };
  232. }
  233. // extra stack space is the space reserved for example when a "public static int TestMethod();" method is called, 4 bytes is pushed, to make room for result;
  234. }
  235. new Cosmos.Assembler.Label(ILOp.GetMethodLabel(aMethod) + EndOfMethodLabelNameException);
  236. if (aMethod.MethodAssembler == null && aMethod.PlugMethod == null && !aMethod.IsInlineAssembler) {
  237. var xBody = aMethod.MethodBase.GetMethodBody();
  238. if (xBody != null) {
  239. uint xLocalsSize = 0;
  240. for (int j = xBody.LocalVariables.Count - 1; j >= 0; j--) {
  241. xLocalsSize += ILOp.Align(ILOp.SizeOfType(xBody.LocalVariables[j].LocalType), 4);
  242. if (xLocalsSize >= 256) {
  243. new Add {
  244. DestinationReg = Registers.ESP,
  245. SourceValue = 255
  246. };
  247. xLocalsSize -= 255;
  248. }
  249. }
  250. if (xLocalsSize > 0) {
  251. new Add {
  252. DestinationReg = Registers.ESP,
  253. SourceValue = xLocalsSize
  254. };
  255. }
  256. }
  257. }
  258. new Cosmos.Assembler.Label(ILOp.GetMethodLabel(aMethod) + EndOfMethodLabelNameException + "__2");
  259. new Pop { DestinationReg = Registers.EBP };
  260. var xRetSize = ((int)xTotalArgsSize) - ((int)xReturnSize);
  261. if (xRetSize < 0) {
  262. xRetSize = 0;
  263. }
  264. WriteDebug(aMethod.MethodBase, (uint)xRetSize, X86.IL.Call.GetStackSizeToReservate(aMethod.MethodBase));
  265. new Return { DestinationValue = (uint)xRetSize };
  266. // Final, after all code. Points to op AFTER method.
  267. new Cosmos.Assembler.Label("GUID_" + mCurrentMethodLabelEndGuid.ToString("N"));
  268. }
  269. public void FinalizeDebugInfo() {
  270. DebugInfo.AddDocument(null, true);
  271. DebugInfo.AddAssemblies(null, true);
  272. DebugInfo.AddMethod(null, true);
  273. DebugInfo.WriteAllLocalsArgumentsInfos(mLocals_Arguments_Infos);
  274. }
  275. public static uint GetResultCodeOffset(uint aResultSize, uint aTotalArgumentSize) {
  276. uint xOffset = 8;
  277. if ((aTotalArgumentSize > 0) && (aTotalArgumentSize >= aResultSize)) {
  278. xOffset += aTotalArgumentSize;
  279. xOffset -= aResultSize;
  280. }
  281. return xOffset;
  282. }
  283. public void ProcessMethod(MethodInfo aMethod, List<ILOpCode> aOpCodes) {
  284. // We check this here and not scanner as when scanner makes these
  285. // plugs may still have not yet been scanned that it will depend on.
  286. // But by the time we make it here, they have to be resolved.
  287. if (aMethod.Type == MethodInfo.TypeEnum.NeedsPlug && aMethod.PlugMethod == null) {
  288. throw new Exception("Method needs plug, but no plug was assigned.");
  289. }
  290. // todo: MtW: how to do this? we need some extra space.
  291. // see ConstructLabel for extra info
  292. if (aMethod.UID > 0x00FFFFFF) {
  293. throw new Exception("Too many methods.");
  294. }
  295. MethodBegin(aMethod);
  296. Assembler.Stack.Clear();
  297. mLog.WriteLine("Method '{0}'", aMethod.MethodBase.GetFullName());
  298. mLog.Flush();
  299. if (aMethod.MethodAssembler != null) {
  300. mLog.WriteLine("Emitted using MethodAssembler", aMethod.MethodBase.GetFullName());
  301. mLog.Flush();
  302. var xAssembler = (AssemblerMethod)Activator.CreateInstance(aMethod.MethodAssembler);
  303. xAssembler.AssembleNew(Assembler, aMethod.PluggedMethod);
  304. } else if (aMethod.IsInlineAssembler) {
  305. mLog.WriteLine("Emitted using Inline MethodAssembler", aMethod.MethodBase.GetFullName());
  306. mLog.Flush();
  307. aMethod.MethodBase.Invoke("", new object[aMethod.MethodBase.GetParameters().Length]);
  308. } else {
  309. foreach (var xOpCode in aOpCodes) {
  310. ushort xOpCodeVal = (ushort)xOpCode.OpCode;
  311. ILOp xILOp;
  312. if (xOpCodeVal <= 0xFF) {
  313. xILOp = mILOpsLo[xOpCodeVal];
  314. } else {
  315. xILOp = mILOpsHi[xOpCodeVal & 0xFF];
  316. }
  317. mLog.WriteLine("\t{0} {1}", Assembler.Stack.Count, xILOp.GetType().Name);
  318. mLog.Flush();
  319. BeforeOp(aMethod, xOpCode);
  320. new Comment(xILOp.ToString());
  321. var xNextPosition = xOpCode.Position + 1;
  322. #region Exception handling support code
  323. ExceptionHandlingClause xCurrentHandler = null;
  324. var xBody = aMethod.MethodBase.GetMethodBody();
  325. // todo: add support for nested handlers using a stack or so..
  326. foreach (ExceptionHandlingClause xHandler in xBody.ExceptionHandlingClauses) {
  327. if (xHandler.TryOffset > 0) {
  328. if (xHandler.TryOffset <= xNextPosition && (xHandler.TryLength + xHandler.TryOffset) > xNextPosition) {
  329. if (xCurrentHandler == null) {
  330. xCurrentHandler = xHandler;
  331. continue;
  332. } else if (xHandler.TryOffset > xCurrentHandler.TryOffset && (xHandler.TryLength + xHandler.TryOffset) < (xCurrentHandler.TryLength + xCurrentHandler.TryOffset)) {
  333. // only replace if the current found handler is narrower
  334. xCurrentHandler = xHandler;
  335. continue;
  336. }
  337. }
  338. }
  339. if (xHandler.HandlerOffset > 0) {
  340. if (xHandler.HandlerOffset <= xNextPosition && (xHandler.HandlerOffset + xHandler.HandlerLength) > xNextPosition) {
  341. if (xCurrentHandler == null) {
  342. xCurrentHandler = xHandler;
  343. continue;
  344. } else if (xHandler.HandlerOffset > xCurrentHandler.HandlerOffset && (xHandler.HandlerOffset + xHandler.HandlerLength) < (xCurrentHandler.HandlerOffset + xCurrentHandler.HandlerLength)) {
  345. // only replace if the current found handler is narrower
  346. xCurrentHandler = xHandler;
  347. continue;
  348. }
  349. }
  350. }
  351. if ((xHandler.Flags & ExceptionHandlingClauseOptions.Filter) > 0) {
  352. if (xHandler.FilterOffset > 0) {
  353. if (xHandler.FilterOffset <= xNextPosition) {
  354. if (xCurrentHandler == null) {
  355. xCurrentHandler = xHandler;
  356. continue;
  357. } else if (xHandler.FilterOffset > xCurrentHandler.FilterOffset) {
  358. // only replace if the current found handler is narrower
  359. xCurrentHandler = xHandler;
  360. continue;
  361. }
  362. }
  363. }
  364. }
  365. }
  366. #endregion
  367. 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));
  368. if (xNeedsExceptionPush) {
  369. Push(DataMember.GetStaticFieldName(ExceptionHelperRefs.CurrentExceptionRef), true);
  370. Assembler.Stack.Push(4, typeof(Exception));
  371. }
  372. xILOp.Execute(aMethod, xOpCode);
  373. AfterOp(aMethod, xOpCode);
  374. //mLog.WriteLine( " end: " + Stack.Count.ToString() );
  375. }
  376. }
  377. MethodEnd(aMethod);
  378. }
  379. protected void InitILOps() {
  380. InitILOps(typeof(ILOp));
  381. }
  382. protected virtual void InitILOps(Type aAssemblerBaseOp) {
  383. foreach (var xType in aAssemblerBaseOp.Assembly.GetExportedTypes()) {
  384. if (xType.IsSubclassOf(aAssemblerBaseOp)) {
  385. var xAttribs = (OpCodeAttribute[])xType.GetCustomAttributes(typeof(OpCodeAttribute), false);
  386. foreach (var xAttrib in xAttribs) {
  387. var xOpCode = (ushort)xAttrib.OpCode;
  388. var xCtor = xType.GetConstructor(new Type[] { typeof(Cosmos.Assembler.Assembler) });
  389. var xILOp = (ILOp)xCtor.Invoke(new Object[] { Assembler });
  390. if (xOpCode <= 0xFF) {
  391. mILOpsLo[xOpCode] = xILOp;
  392. } else {
  393. mILOpsHi[xOpCode & 0xFF] = xILOp;
  394. }
  395. }
  396. }
  397. }
  398. }
  399. protected void Move(string aDestLabelName, int aValue) {
  400. new Mov {
  401. DestinationRef = ElementReference.New(aDestLabelName),
  402. DestinationIsIndirect = true,
  403. SourceValue = (uint)aValue
  404. };
  405. }
  406. protected void Push(uint aValue) {
  407. new Push {
  408. DestinationValue = aValue
  409. };
  410. }
  411. protected void Pop() {
  412. new Add { DestinationReg = Registers.ESP, SourceValue = (uint)Assembler.Stack.Pop().Size };
  413. }
  414. protected void Push(string aLabelName, bool isIndirect = false) {
  415. new Push {
  416. DestinationRef = ElementReference.New(aLabelName),
  417. DestinationIsIndirect = isIndirect
  418. };
  419. }
  420. protected void Call(MethodBase aMethod) {
  421. new Cosmos.Assembler.x86.Call {
  422. DestinationLabel = LabelName.Get(aMethod)
  423. };
  424. }
  425. protected void Jump(string aLabelName) {
  426. new Cosmos.Assembler.x86.Jump {
  427. DestinationLabel = aLabelName
  428. };
  429. }
  430. protected void Ldarg(MethodInfo aMethod, int aIndex) {
  431. X86.IL.Ldarg.DoExecute(Assembler, aMethod, (ushort)aIndex);
  432. }
  433. protected void Call(MethodInfo aMethod, MethodInfo aTargetMethod) {
  434. var xSize = X86.IL.Call.GetStackSizeToReservate(aTargetMethod.MethodBase);
  435. if (xSize > 0) {
  436. new Sub { DestinationReg = Registers.ESP, SourceValue = xSize };
  437. }
  438. new Call { DestinationLabel = ILOp.GetMethodLabel(aTargetMethod) };
  439. }
  440. protected void Ldflda(MethodInfo aMethod, string aFieldId) {
  441. X86.IL.Ldflda.DoExecute(Assembler, aMethod, aMethod.MethodBase.DeclaringType, aFieldId, false);
  442. }
  443. protected int GetVTableEntrySize() {
  444. return 16; // todo: retrieve from actual type info
  445. }
  446. public const string InitVMTCodeLabel = "___INIT__VMT__CODE____";
  447. public virtual void GenerateVMTCode(HashSet<Type> aTypesSet, HashSet<MethodBase> aMethodsSet, Func<Type, uint> aGetTypeID, Func<MethodBase, uint> aGetMethodUID) {
  448. new Comment("---------------------------------------------------------");
  449. new Cosmos.Assembler.Label(InitVMTCodeLabel);
  450. new Push { DestinationReg = Registers.EBP };
  451. new Mov { DestinationReg = Registers.EBP, SourceReg = Registers.ESP };
  452. mSequences = new DebugInfo.SequencePoint[0];
  453. var xSetTypeInfoRef = VTablesImplRefs.SetTypeInfoRef;
  454. var xSetMethodInfoRef = VTablesImplRefs.SetMethodInfoRef;
  455. var xLoadTypeTableRef = VTablesImplRefs.LoadTypeTableRef;
  456. var xTypesFieldRef = VTablesImplRefs.VTablesImplDef.GetField("mTypes",
  457. BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
  458. string xTheName = DataMember.GetStaticFieldName(xTypesFieldRef);
  459. DataMember xDataMember = (from item in Cosmos.Assembler.Assembler.CurrentInstance.DataMembers
  460. where item.Name == xTheName
  461. select item).FirstOrDefault();
  462. if (xDataMember != null) {
  463. Cosmos.Assembler.Assembler.CurrentInstance.DataMembers.Remove((from item in Cosmos.Assembler.Assembler.CurrentInstance.DataMembers
  464. where item == xDataMember
  465. select item).First());
  466. }
  467. var xData = new byte[16 + (aTypesSet.Count * GetVTableEntrySize())];
  468. var xTemp = BitConverter.GetBytes(aGetTypeID(typeof(Array)));
  469. xTemp = BitConverter.GetBytes(0x80000002);
  470. Array.Copy(xTemp, 0, xData, 4, 4);
  471. xTemp = BitConverter.GetBytes(aTypesSet.Count);
  472. Array.Copy(xTemp, 0, xData, 8, 4);
  473. xTemp = BitConverter.GetBytes(GetVTableEntrySize());
  474. Array.Copy(xTemp, 0, xData, 12, 4);
  475. Cosmos.Assembler.Assembler.CurrentInstance.DataMembers.Add(new DataMember(xTheName + "__Contents", xData));
  476. Cosmos.Assembler.Assembler.CurrentInstance.DataMembers.Add(new DataMember(xTheName, Cosmos.Assembler.ElementReference.New(xTheName + "__Contents")));
  477. #if VMT_DEBUG
  478. using (var xVmtDebugOutput = XmlWriter.Create(@"e:\vmt_debug.xml"))
  479. {
  480. xVmtDebugOutput.WriteStartDocument();
  481. xVmtDebugOutput.WriteStartElement("VMT");
  482. #endif
  483. //Push((uint)aTypesSet.Count);
  484. //Call(xLoadTypeTableRef);
  485. foreach (var xType in aTypesSet) {
  486. #if VMT_DEBUG
  487. xVmtDebugOutput.WriteStartElement("Type");
  488. xVmtDebugOutput.WriteAttributeString("TypeId", aGetTypeID(xType).ToString());
  489. if (xType.BaseType != null)
  490. {
  491. xVmtDebugOutput.WriteAttributeString("BaseTypeId", aGetTypeID(xType.BaseType).ToString());
  492. }
  493. xVmtDebugOutput.WriteAttributeString("Name", xType.FullName);
  494. #endif
  495. // value contains true if the method is an interface method definition
  496. SortedList<MethodBase, bool> xEmittedMethods = new SortedList<MethodBase, bool>(new MethodBaseComparer());
  497. foreach (MethodBase xMethod in xType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) {
  498. if (aMethodsSet.Contains(xMethod)) { //) && !xMethod.IsAbstract) {
  499. xEmittedMethods.Add(xMethod, false);
  500. }
  501. }
  502. foreach (MethodBase xCtor in xType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) {
  503. if (aMethodsSet.Contains(xCtor)) { // && !xCtor.IsAbstract) {
  504. xEmittedMethods.Add(xCtor, false);
  505. }
  506. }
  507. foreach (var xIntf in xType.GetInterfaces()) {
  508. foreach (var xMethodIntf in xIntf.GetMethods()) {
  509. var xActualMethod = xType.GetMethod(xIntf.FullName + "." + xMethodIntf.Name,
  510. (from xParam in xMethodIntf.GetParameters()
  511. select xParam.ParameterType).ToArray());
  512. if (xActualMethod == null) {
  513. // get private implemenation
  514. xActualMethod = xType.GetMethod(xMethodIntf.Name,
  515. (from xParam in xMethodIntf.GetParameters()
  516. select xParam.ParameterType).ToArray());
  517. }
  518. if (xActualMethod == null) {
  519. try {
  520. var xMap = xType.GetInterfaceMap(xIntf);
  521. for (int k = 0; k < xMap.InterfaceMethods.Length; k++) {
  522. if (xMap.InterfaceMethods[k] == xMethodIntf) {
  523. xActualMethod = xMap.TargetMethods[k];
  524. break;
  525. }
  526. }
  527. } catch {
  528. }
  529. }
  530. if (aMethodsSet.Contains(xMethodIntf)) {
  531. if (!xEmittedMethods.ContainsKey(xMethodIntf)) {
  532. xEmittedMethods.Add(xMethodIntf, true);
  533. }
  534. }
  535. }
  536. }
  537. if (!xType.IsInterface) {
  538. Push(aGetTypeID(xType));
  539. }
  540. int? xBaseIndex = null;
  541. if (xType.BaseType == null) {
  542. xBaseIndex = (int)aGetTypeID(xType);
  543. } else {
  544. for (int t = 0; t < aTypesSet.Count; t++) {
  545. // todo: optimize check
  546. var xItem = aTypesSet.Skip(t).First();
  547. if (xItem.ToString() == xType.BaseType.ToString()) {
  548. xBaseIndex = (int)aGetTypeID(xItem);
  549. break;
  550. }
  551. }
  552. }
  553. if (xBaseIndex == null) {
  554. throw new Exception("Base type not found!");
  555. }
  556. for (int x = xEmittedMethods.Count - 1; x >= 0; x--) {
  557. if (!aMethodsSet.Contains(xEmittedMethods.Keys[x])) {
  558. xEmittedMethods.RemoveAt(x);
  559. }
  560. }
  561. if (!xType.IsInterface) {
  562. Move("VMT__TYPE_ID_HOLDER__" + DataMember.FilterStringForIncorrectChars(LabelName.GetFullName(xType) + " ASM_IS__" + xType.Assembly.GetName().Name), (int)aGetTypeID(xType));
  563. Cosmos.Assembler.Assembler.CurrentInstance.DataMembers.Add(
  564. new DataMember("VMT__TYPE_ID_HOLDER__" + DataMember.FilterStringForIncorrectChars(LabelName.GetFullName(xType) + " ASM_IS__" + xType.Assembly.GetName().Name), new int[] { (int)aGetTypeID(xType) }));
  565. Push((uint)xBaseIndex.Value);
  566. xData = new byte[16 + (xEmittedMethods.Count * 4)];
  567. xTemp = BitConverter.GetBytes(aGetTypeID(typeof(Array)));
  568. Array.Copy(xTemp, 0, xData, 0, 4);
  569. xTemp = BitConverter.GetBytes(0x80000002); // embedded array
  570. Array.Copy(xTemp, 0, xData, 4, 4);
  571. xTemp = BitConverter.GetBytes(xEmittedMethods.Count); // embedded array
  572. Array.Copy(xTemp, 0, xData, 8, 4);
  573. xTemp = BitConverter.GetBytes(4); // embedded array
  574. Array.Copy(xTemp, 0, xData, 12, 4);
  575. string xDataName = "____SYSTEM____TYPE___" + DataMember.FilterStringForIncorrectChars(LabelName.GetFullName(xType) + " ASM_IS__" + xType.Assembly.GetName().Name) + "__MethodIndexesArray";
  576. Cosmos.Assembler.Assembler.CurrentInstance.DataMembers.Add(new DataMember(xDataName, xData));
  577. Push(xDataName);
  578. xDataName = "____SYSTEM____TYPE___" + DataMember.FilterStringForIncorrectChars(LabelName.GetFullName(xType) + " ASM_IS__" + xType.Assembly.GetName().Name) + "__MethodAddressesArray";
  579. Cosmos.Assembler.Assembler.CurrentInstance.DataMembers.Add(new DataMember(xDataName, xData));
  580. Push(xDataName);
  581. xData = new byte[16 + Encoding.Unicode.GetByteCount(xType.FullName + ", " + xType.Module.Assembly.GetName().FullName)];
  582. xTemp = BitConverter.GetBytes(aGetTypeID(typeof(Array)));
  583. Array.Copy(xTemp, 0, xData, 0, 4);
  584. xTemp = BitConverter.GetBytes(0x80000002); // embedded array
  585. Array.Copy(xTemp, 0, xData, 4, 4);
  586. xTemp = BitConverter.GetBytes((xType.FullName + ", " + xType.Module.Assembly.GetName().FullName).Length);
  587. Array.Copy(xTemp, 0, xData, 8, 4);
  588. xTemp = BitConverter.GetBytes(2); // embedded array
  589. Array.Copy(xTemp, 0, xData, 12, 4);
  590. xDataName = "____SYSTEM____TYPE___" + DataMember.FilterStringForIncorrectChars(LabelName.GetFullName(xType) + " ASM_IS__" + xType.Assembly.GetName().Name);
  591. Cosmos.Assembler.Assembler.CurrentInstance.DataMembers.Add(new DataMember(xDataName, xData));
  592. Push("0" + xEmittedMethods.Count.ToString("X") + "h");
  593. Call(xSetTypeInfoRef);
  594. }
  595. for (int j = 0; j < xEmittedMethods.Count; j++) {
  596. MethodBase xMethod = xEmittedMethods.Keys[j];
  597. #if VMT_DEBUG
  598. xVmtDebugOutput.WriteStartElement("Method");
  599. xVmtDebugOutput.WriteAttributeString("Id", aGetMethodUID(xMethod).ToString());
  600. xVmtDebugOutput.WriteAttributeString("Name", xMethod.GetFullName());
  601. xVmtDebugOutput.WriteEndElement();
  602. #endif
  603. var xMethodId = aGetMethodUID(xMethod);
  604. if (!xType.IsInterface) {
  605. if (xEmittedMethods.Values[j]) {
  606. var xNewMethod = xType.GetMethod(xMethod.DeclaringType.FullName + "." + xMethod.Name,
  607. (from xParam in xMethod.GetParameters()
  608. select xParam.ParameterType).ToArray());
  609. if (xNewMethod == null) {
  610. // get private implementation
  611. xNewMethod = xType.GetMethod(xMethod.Name,
  612. (from xParam in xMethod.GetParameters()
  613. select xParam.ParameterType).ToArray());
  614. }
  615. if (xNewMethod == null) {
  616. try {
  617. var xMap = xType.GetInterfaceMap(xMethod.DeclaringType);
  618. for (int k = 0; k < xMap.InterfaceMethods.Length; k++) {
  619. if (xMap.InterfaceMethods[k] == xMethod) {
  620. xNewMethod = xMap.TargetMethods[k];
  621. break;
  622. }
  623. }
  624. } catch {
  625. }
  626. }
  627. xMethod = xNewMethod;
  628. }
  629. Push((uint)aGetTypeID(xType));
  630. Push((uint)j);
  631. Push((uint)xMethodId);
  632. if (xMethod.IsAbstract) {
  633. // abstract methods dont have bodies, oiw, are not emitted
  634. Push(0);
  635. } else {
  636. Push(ILOp.GetMethodLabel(xMethod));
  637. }
  638. Push(0);
  639. Call(VTablesImplRefs.SetMethodInfoRef);
  640. }
  641. }
  642. #if VMT_DEBUG
  643. xVmtDebugOutput.WriteEndElement(); // type
  644. #endif
  645. }
  646. #if VMT_DEBUG
  647. xVmtDebugOutput.WriteEndElement(); // types
  648. xVmtDebugOutput.WriteEndDocument();
  649. }
  650. #endif
  651. new Cosmos.Assembler.Label("_END_OF_" + InitVMTCodeLabel);
  652. new Pop { DestinationReg = Registers.EBP };
  653. new Return();
  654. }
  655. public void ProcessField(FieldInfo aField) {
  656. string xFieldName = LabelName.GetFullName(aField);
  657. xFieldName = DataMember.GetStaticFieldName(aField);
  658. if (Cosmos.Assembler.Assembler.CurrentInstance.DataMembers.Count(x => x.Name == xFieldName) == 0) {
  659. var xItemList = (from item in aField.GetCustomAttributes(false)
  660. where item.GetType().FullName == "ManifestResourceStreamAttribute"
  661. select item).ToList();
  662. object xItem = null;
  663. if (xItemList.Count > 0)
  664. xItem = xItemList[0];
  665. string xManifestResourceName = null;
  666. if (xItem != null) {
  667. var xItemType = xItem.GetType();
  668. xManifestResourceName = (string)xItemType.GetField("ResourceName").GetValue(xItem);
  669. }
  670. if (xManifestResourceName != null) {
  671. // todo: add support for manifest streams again
  672. //RegisterType(xCurrentField.FieldType);
  673. //string xFileName = Path.Combine(mOutputDir,
  674. // (xCurrentField.DeclaringType.Assembly.FullName + "__" + xManifestResourceName).Replace(",",
  675. // "_") + ".res");
  676. //using (var xStream = xCurrentField.DeclaringType.Assembly.GetManifestResourceStream(xManifestResourceName)) {
  677. // if (xStream == null) {
  678. // throw new Exception("Resource '" + xManifestResourceName + "' not found!");
  679. // }
  680. // using (var xTarget = File.Create(xFileName)) {
  681. // // todo: abstract this array code out.
  682. // xTarget.Write(BitConverter.GetBytes(Engine.RegisterType(Engine.GetType("mscorlib",
  683. // "System.Array"))),
  684. // 0,
  685. // 4);
  686. // xTarget.Write(BitConverter.GetBytes((uint)InstanceTypeEnum.StaticEmbeddedArray),
  687. // 0,
  688. // 4);
  689. // xTarget.Write(BitConverter.GetBytes((int)xStream.Length), 0, 4);
  690. // xTarget.Write(BitConverter.GetBytes((int)1), 0, 4);
  691. // var xBuff = new byte[128];
  692. // while (xStream.Position < xStream.Length) {
  693. // int xBytesRead = xStream.Read(xBuff, 0, 128);
  694. // xTarget.Write(xBuff, 0, xBytesRead);
  695. // }
  696. // }
  697. //}
  698. //Assembler.DataMembers.Add(new DataMember("___" + xFieldName + "___Contents",
  699. // "incbin",
  700. // "\"" + xFileName + "\""));
  701. //Assembler.DataMembers.Add(new DataMember(xFieldName,
  702. // "dd",
  703. // "___" + xFieldName + "___Contents"));
  704. throw new NotImplementedException();
  705. } else {
  706. uint xTheSize;
  707. //string theType = "db";
  708. Type xFieldTypeDef = aField.FieldType;
  709. if (!xFieldTypeDef.IsClass || xFieldTypeDef.IsValueType) {
  710. xTheSize = GetSizeOfType(aField.FieldType);
  711. } else {
  712. xTheSize = 4;
  713. }
  714. byte[] xData = new byte[xTheSize];
  715. try {
  716. object xValue = aField.GetValue(null);
  717. if (xValue != null) {
  718. try {
  719. xData = new byte[xTheSize];
  720. if (xValue.GetType().IsValueType) {
  721. for (int x = 0; x < xTheSize; x++) {
  722. xData[x] = Marshal.ReadByte(xValue,
  723. x);
  724. }
  725. }
  726. } catch {
  727. }
  728. }
  729. } catch {
  730. }
  731. Cosmos.Assembler.Assembler.CurrentInstance.DataMembers.Add(new DataMember(xFieldName, xData));
  732. }
  733. }
  734. }
  735. public uint GetSizeOfType(Type aType) {
  736. return ILOp.SizeOfType(aType);
  737. }
  738. internal void GenerateMethodForward(MethodInfo aFrom, MethodInfo aTo) {
  739. // todo: completely get rid of this kind of trampoline code
  740. MethodBegin(aFrom);
  741. {
  742. var xParams = aTo.MethodBase.GetParameters().ToArray();
  743. if (aTo.MethodAssembler != null) {
  744. xParams = aFrom.MethodBase.GetParameters();
  745. }
  746. //if (aFrom.MethodBase.GetParameters().Length > 0 || !aFrom.MethodBase.IsStatic) {
  747. // Ldarg(aFrom, 0);
  748. // Pop();
  749. //}
  750. int xCurParamIdx = 0;
  751. if (!aFrom.MethodBase.IsStatic) {
  752. Ldarg(aFrom, 0);
  753. xCurParamIdx++;
  754. if (aTo.MethodAssembler == null) {
  755. xParams = xParams.Skip(1).ToArray();
  756. }
  757. }
  758. foreach (var xParam in xParams) {
  759. FieldAccessAttribute xFieldAccessAttrib = null;
  760. foreach (var xAttrib in xParam.GetCustomAttributes(typeof(FieldAccessAttribute), true)) {
  761. xFieldAccessAttrib = xAttrib as FieldAccessAttribute;
  762. }
  763. if (xFieldAccessAttrib != null) {
  764. // field access
  765. new Comment("Loading address of field '" + xFieldAccessAttrib.Name + "'");
  766. Ldarg(aFrom, 0);
  767. Ldflda(aFrom, xFieldAccessAttrib.Name);
  768. } else {
  769. // normal field access
  770. new Comment("Loading parameter " + xCurParamIdx);
  771. Ldarg(aFrom, xCurParamIdx);
  772. xCurParamIdx++;
  773. }
  774. }
  775. Call(aFrom, aTo);
  776. }
  777. MethodEnd(aFrom);
  778. }
  779. protected static void WriteDebug(MethodBase aMethod, uint aSize, uint aSize2) {
  780. var xLine = String.Format("{0}\t{1}\t{2}", LabelName.GenerateFullName(aMethod), aSize, aSize2);
  781. }
  782. // These are all temp functions until we move to the new assembler.
  783. // They are used to clean up the old assembler slightly while retaining compatibiltiy for now
  784. public static string TmpPosLabel(MethodInfo aMethod, int aOffset) {
  785. return ILOp.GetLabel(aMethod, aOffset);
  786. }
  787. public static string TmpPosLabel(MethodInfo aMethod, ILOpCode aOpCode) {
  788. return TmpPosLabel(aMethod, aOpCode.Position);
  789. }
  790. public static string TmpBranchLabel(MethodInfo aMethod, ILOpCode aOpCode) {
  791. return TmpPosLabel(aMethod, ((ILOpCodes.OpBranch)aOpCode).Value);
  792. }
  793. public void EmitEntrypoint(MethodBase aEntrypoint) {
  794. // at the time the datamembers for literal strings are created, the type id for string is not yet determined.
  795. // for now, we fix this at runtime.
  796. new Cosmos.Assembler.Label(InitStringIDsLabel);
  797. new Push { DestinationReg = Registers.EBP };
  798. new Mov { DestinationReg = Registers.EBP, SourceReg = Registers.ESP };
  799. new Mov { DestinationReg = Registers.EAX, SourceRef = Cosmos.Assembler.ElementReference.New(ILOp.GetTypeIDLabel(typeof(String))), SourceIsIndirect = true };
  800. new Mov { DestinationRef = ElementReference.New("static_field__System_String_Empty"), DestinationIsIndirect = true, SourceRef = ElementReference.New(X86.IL.LdStr.GetContentsArrayName("")) };
  801. foreach (var xDataMember in Assembler.DataMembers) {
  802. if (!xDataMember.Name.StartsWith("StringLiteral")) {
  803. continue;
  804. }
  805. if (xDataMember.Name.EndsWith("__Contents")) {
  806. continue;
  807. }
  808. new Mov { DestinationRef = Cosmos.Assembler.ElementReference.New(xDataMember.Name), DestinationIsIndirect = true, SourceReg = Registers.EAX };
  809. }
  810. new Pop { DestinationReg = Registers.EBP };
  811. new Return();
  812. new Cosmos.Assembler.Label(Cosmos.Assembler.Assembler.EntryPointName);
  813. new Push { DestinationReg = Registers.EBP };
  814. new Mov { DestinationReg = Registers.EBP, SourceReg = Registers.ESP };
  815. new Call { DestinationLabel = InitVMTCodeLabel };
  816. Cosmos.Assembler.Assembler.WriteDebugVideo("Initializing string IDs.");
  817. new Call { DestinationLabel = InitStringIDsLabel };
  818. // we now need to do "newobj" on the entry point, and after that, call .Start on it
  819. var xCurLabel = Cosmos.Assembler.Assembler.EntryPointName + ".CreateEntrypoint";
  820. new Cosmos.Assembler.Label(xCurLabel);
  821. X86.IL.Newobj.Assemble(Cosmos.Assembler.Assembler.CurrentInstance, null, null, xCurLabel, aEntrypoint.DeclaringType, aEntrypoint);
  822. xCurLabel = Cosmos.Assembler.Assembler.EntryPointName + ".CallStart";
  823. new Cosmos.Assembler.Label(xCurLabel);
  824. X86.IL.Call.DoExecute(Assembler, null, aEntrypoint.DeclaringType.BaseType.GetMethod("Start"), null, xCurLabel, Cosmos.Assembler.Assembler.EntryPointName + ".AfterStart");
  825. new Cosmos.Assembler.Label(Cosmos.Assembler.Assembler.EntryPointName + ".AfterStart");
  826. new Pop { DestinationReg = Registers.EBP };
  827. new Return();
  828. if (ShouldOptimize) {
  829. Orvid.Optimizer.Optimize(Assembler);
  830. }
  831. }
  832. protected void AfterOp(MethodInfo aMethod, ILOpCode aOpCode) {
  833. var xContents = "";
  834. foreach (var xStackItem in Assembler.Stack) {
  835. xContents += ILOp.Align((uint)xStackItem.Size, 4);
  836. xContents += ", ";
  837. }
  838. if (xContents.EndsWith(", ")) {
  839. xContents = xContents.Substring(0, xContents.Length - 2);
  840. }
  841. new Comment("Stack contains " + Assembler.Stack.Count + " items: (" + xContents + ")");
  842. }
  843. protected void BeforeOp(MethodInfo aMethod, ILOpCode aOpCode) {
  844. string xLabel = TmpPosLabel(aMethod, aOpCode);
  845. Assembler.CurrentIlLabel = xLabel;
  846. new Cosmos.Assembler.Label(xLabel);
  847. if (mSymbols != null) {
  848. var xMLSymbol = new MethodIlOp();
  849. xMLSymbol.LabelName = TmpPosLabel(aMethod, aOpCode);
  850. var xStackSize = (from item in Assembler.Stack
  851. let xSize = (item.Size % 4u == 0u) ? item.Size : (item.Size + (4u - (item.Size % 4u)))
  852. select xSize).Sum();
  853. xMLSymbol.StackDiff = -1;
  854. if (aMethod.MethodBase != null) {
  855. var xBody = aMethod.MethodBase.GetMethodBody();
  856. if (xBody != null) {
  857. var xLocalsSize = (from item in xBody.LocalVariables
  858. select ILOp.Align(ILOp.SizeOfType(item.LocalType), 4)).Sum();
  859. xMLSymbol.StackDiff = checked((int)(xLocalsSize + xStackSize));
  860. }
  861. }
  862. xMLSymbol.IlOffset = aOpCode.Position;
  863. xMLSymbol.MethodID = mCurrentMethodGuid;
  864. mSymbols.Add(xMLSymbol);
  865. DebugInfo.AddSymbols(mSymbols);
  866. }
  867. DebugInfo.AddSymbols(mSymbols, false);
  868. EmitTracer(aMethod, aOpCode, aMethod.MethodBase.DeclaringType.Namespace);
  869. }
  870. protected void EmitTracer(MethodInfo aMethod, ILOpCode aOp, string aNamespace) {
  871. // NOTE - These if statements can be optimized down - but clarity is
  872. // more important the optimizations. Furthermoer the optimazations available
  873. // would not offer much benefit
  874. // Determine if a new DebugStub should be emitted
  875. if (aOp.OpCode == ILOpCode.Code.Nop) {
  876. // Skip NOOP's so we dont have breakpoints on them
  877. //TODO: Each IL op should exist in IL, and descendants in IL.X86.
  878. // Because of this we have this hack
  879. return;
  880. } else if (DebugEnabled == false) {
  881. return;
  882. } else if (DebugMode == DebugMode.Source) {
  883. // If the current position equals one of the offsets, then we have
  884. // reached a new atomic C# statement
  885. var xSP = mSequences.SingleOrDefault(q => q.Offset == aOp.Position);
  886. if (xSP == null) {
  887. return;
  888. } else if (xSP.LineStart == 0xFEEFEE) {
  889. // 0xFEEFEE means hiddenline -> we dont want to stop there
  890. return;
  891. }
  892. }
  893. // Check if the DebugStub has been disabled for this method
  894. if ((!IgnoreDebugStubAttribute) && (aMethod.DebugStubOff)) {
  895. return;
  896. }
  897. // This test fixes issue #15638
  898. if (null != aNamespace)
  899. {
  900. // Check options for Debug Level
  901. // Set based on TracedAssemblies
  902. if (TraceAssemblies == TraceAssemblies.Cosmos || TraceAssemblies == TraceAssemblies.User)
  903. {
  904. if (aNamespace.StartsWith("System.", StringComparison.InvariantCultureIgnoreCase)) {
  905. return;
  906. }
  907. else if (aNamespace.ToLower() == "system") {
  908. return;
  909. }
  910. else if (aNamespace.StartsWith("Microsoft.", StringComparison.InvariantCultureIgnoreCase)) {
  911. return;
  912. }
  913. if (TraceAssemblies == TraceAssemblies.User)
  914. {
  915. //TODO: Maybe an attribute that could be used to turn tracing on and off
  916. //TODO: This doesnt match Cosmos.Kernel exact vs Cosmos.Kernel., so a user
  917. // could do Cosmos.KernelMine and it will fail. Need to fix this
  918. if (aNamespace.StartsWith("Cosmos.Kernel", StringComparison.InvariantCultureIgnoreCase)) {
  919. return;
  920. }
  921. else if (aNamespace.StartsWith("Cosmos.Sys", StringComparison.InvariantCultureIgnoreCase)) {
  922. return;
  923. }
  924. else if (aNamespace.StartsWith("Cosmos.Hardware", StringComparison.InvariantCultureIgnoreCase)) {
  925. return;
  926. }
  927. else if (aNamespace.StartsWith("Cosmos.IL2CPU", StringComparison.InvariantCultureIgnoreCase)) {
  928. return;
  929. }
  930. }
  931. }
  932. }
  933. // If we made it this far without a return, emit the Tracer
  934. new INT3();
  935. }
  936. protected MethodDefinition GetCecilMethodDefinitionForSymbolReading(MethodBase methodBase) {
  937. var xMethodBase = methodBase;
  938. if (xMethodBase.IsGenericMethod) {
  939. var xMethodInfo = (System.Reflection.MethodInfo)xMethodBase;
  940. xMethodBase = xMethodInfo.GetGenericMethodDefinition();
  941. if (xMethodBase.IsGenericMethod) {
  942. // apparently, a generic method can be derived from a generic method..
  943. throw new Exception("Make recursive");
  944. }
  945. }
  946. var xLocation = xMethodBase.DeclaringType.Assembly.Location;
  947. ModuleDefinition xModule = null;
  948. if (!mLoadedModules.TryGetValue(xLocation, out xModule)) {
  949. // if not in cache, try loading.
  950. if (xMethodBase.DeclaringType.Assembly.GlobalAssemblyCache || !File.Exists(xLocation)) {
  951. // file doesn't exist, so assume no symbols
  952. mLoadedModules.Add(xLocation, null);
  953. return null;
  954. } else {
  955. try {
  956. xModule = ModuleDefinition.ReadModule(xLocation, new ReaderParameters { ReadSymbols = true, SymbolReaderProvider = new Mono.Cecil.Pdb.PdbReaderProvider() });
  957. } catch (InvalidOperationException) {
  958. throw new Exception("Please check that dll and pdb file is matching on location: " + xLocation);
  959. }
  960. if (xModule.HasSymbols) {
  961. mLoadedModules.Add(xLocation, xModule);
  962. } else {
  963. mLoadedModules.Add(xLocation, null);
  964. return null;
  965. }
  966. }
  967. }
  968. if (xModule == null) {
  969. return null;
  970. }
  971. // todo: cache MethodDefinition ?
  972. return xModule.LookupToken(xMethodBase.MetadataToken) as MethodDefinition;
  973. }
  974. }
  975. }