ILVisualizer.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  2. using System.Collections.Immutable;
  3. //using Roslyn.Utilities;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.Globalization;
  8. using System.Text;
  9. using System.Reflection.Emit;
  10. using System.Reflection;
  11. using System.Reflection.Metadata;
  12. using System.Reflection.Metadata.Ecma335;
  13. namespace AtomicEditor
  14. {
  15. public abstract class ILVisualizer
  16. {
  17. private static readonly OpCode[] s_oneByteOpCodes;
  18. private static readonly OpCode[] s_twoByteOpCodes;
  19. static ILVisualizer()
  20. {
  21. s_oneByteOpCodes = new OpCode[0x100];
  22. s_twoByteOpCodes = new OpCode[0x100];
  23. var typeOfOpCode = typeof(OpCode);
  24. foreach (FieldInfo fi in typeof(OpCodes).GetTypeInfo().DeclaredFields)
  25. {
  26. if (fi.FieldType != typeOfOpCode)
  27. {
  28. continue;
  29. }
  30. OpCode opCode = (OpCode)fi.GetValue(null);
  31. var value = unchecked((ushort)opCode.Value);
  32. if (value < 0x100)
  33. {
  34. s_oneByteOpCodes[value] = opCode;
  35. }
  36. else if ((value & 0xff00) == 0xfe00)
  37. {
  38. s_twoByteOpCodes[value & 0xff] = opCode;
  39. }
  40. }
  41. }
  42. public enum HandlerKind
  43. {
  44. Try,
  45. Catch,
  46. Filter,
  47. Finally,
  48. Fault
  49. }
  50. public struct HandlerSpan : IComparable<HandlerSpan>
  51. {
  52. public readonly HandlerKind Kind;
  53. public readonly object ExceptionType;
  54. public readonly int StartOffset;
  55. public readonly int FilterHandlerStart;
  56. public readonly int EndOffset;
  57. public HandlerSpan(HandlerKind kind, object exceptionType, int startOffset, int endOffset, int filterHandlerStart = 0)
  58. {
  59. this.Kind = kind;
  60. this.ExceptionType = exceptionType;
  61. this.StartOffset = startOffset;
  62. this.EndOffset = endOffset;
  63. this.FilterHandlerStart = filterHandlerStart;
  64. }
  65. public int CompareTo(HandlerSpan other)
  66. {
  67. int result = this.StartOffset - other.StartOffset;
  68. if (result == 0)
  69. {
  70. // Both blocks have same start. Order larger (outer) before smaller (inner).
  71. result = other.EndOffset - this.EndOffset;
  72. }
  73. return result;
  74. }
  75. public string ToString(ILVisualizer visualizer)
  76. {
  77. switch (this.Kind)
  78. {
  79. default:
  80. return ".try";
  81. case HandlerKind.Catch:
  82. return "catch " + visualizer.VisualizeLocalType(this.ExceptionType);
  83. case HandlerKind.Filter:
  84. return "filter";
  85. case HandlerKind.Finally:
  86. return "finally";
  87. case HandlerKind.Fault:
  88. return "fault";
  89. }
  90. }
  91. public override string ToString()
  92. {
  93. throw new NotSupportedException("Use ToString(ILVisualizer)");
  94. }
  95. }
  96. public struct LocalInfo
  97. {
  98. public readonly string Name;
  99. public readonly bool IsPinned;
  100. public readonly bool IsByRef;
  101. public readonly object Type; // ITypeReference or ITypeSymbol
  102. public LocalInfo(string name, object type, bool isPinned, bool isByRef)
  103. {
  104. Name = name;
  105. Type = type;
  106. IsPinned = isPinned;
  107. IsByRef = isByRef;
  108. }
  109. }
  110. public const string IndentString = " ";
  111. public abstract string VisualizeUserString(uint token);
  112. public abstract string VisualizeSymbol(uint token);
  113. public abstract string VisualizeLocalType(object type);
  114. private static ulong ReadUInt64(ImmutableArray<byte> buffer, ref int pos)
  115. {
  116. ulong result =
  117. buffer[pos] |
  118. (ulong)buffer[pos + 1] << 8 |
  119. (ulong)buffer[pos + 2] << 16 |
  120. (ulong)buffer[pos + 3] << 24 |
  121. (ulong)buffer[pos + 4] << 32 |
  122. (ulong)buffer[pos + 5] << 40 |
  123. (ulong)buffer[pos + 6] << 48 |
  124. (ulong)buffer[pos + 7] << 56;
  125. pos += sizeof(ulong);
  126. return result;
  127. }
  128. private static uint ReadUInt32(ImmutableArray<byte> buffer, ref int pos)
  129. {
  130. uint result = buffer[pos] | (uint)buffer[pos + 1] << 8 | (uint)buffer[pos + 2] << 16 | (uint)buffer[pos + 3] << 24;
  131. pos += sizeof(uint);
  132. return result;
  133. }
  134. private static int ReadInt32(ImmutableArray<byte> buffer, ref int pos)
  135. {
  136. return unchecked((int)ReadUInt32(buffer, ref pos));
  137. }
  138. private static ushort ReadUInt16(ImmutableArray<byte> buffer, ref int pos)
  139. {
  140. ushort result = (ushort)(buffer[pos]| buffer[pos + 1] << 8);
  141. pos += sizeof(ushort);
  142. return result;
  143. }
  144. private static byte ReadByte(ImmutableArray<byte> buffer, ref int pos)
  145. {
  146. byte result = buffer[pos];
  147. pos += sizeof(byte);
  148. return result;
  149. }
  150. private static sbyte ReadSByte(ImmutableArray<byte> buffer, ref int pos)
  151. {
  152. sbyte result = unchecked((sbyte)buffer[pos]);
  153. pos += 1;
  154. return result;
  155. }
  156. private unsafe static float ReadSingle(ImmutableArray<byte> buffer, ref int pos)
  157. {
  158. uint value = ReadUInt32(buffer, ref pos);
  159. return *(float*)&value;
  160. }
  161. private unsafe static double ReadDouble(ImmutableArray<byte> buffer, ref int pos)
  162. {
  163. ulong value = ReadUInt64(buffer, ref pos);
  164. return *(double*)&value;
  165. }
  166. public void VisualizeHeader(StringBuilder sb, int codeSize, int maxStack, ImmutableArray<LocalInfo> locals)
  167. {
  168. if (codeSize >= 0 && maxStack >= 0)
  169. {
  170. if (codeSize == 0)
  171. {
  172. sb.AppendLine(" // Unrealized IL");
  173. }
  174. else
  175. {
  176. sb.AppendLine(string.Format(" // Code size {0,8} (0x{0:x})", codeSize));
  177. }
  178. sb.AppendLine(string.Format(" .maxstack {0}", maxStack));
  179. }
  180. int i = 0;
  181. foreach (var local in locals)
  182. {
  183. sb.Append(i == 0 ? " .locals init (" : new string(' ', " .locals init (".Length));
  184. if (local.IsPinned)
  185. {
  186. sb.Append("pinned ");
  187. }
  188. sb.Append(VisualizeLocalType(local.Type));
  189. if (local.IsByRef)
  190. {
  191. sb.Append("&");
  192. }
  193. sb.Append(" ");
  194. sb.Append("V_" + i);
  195. sb.Append(i == locals.Length - 1 ? ")" : ",");
  196. var name = local.Name;
  197. if (name != null)
  198. {
  199. sb.Append(" //");
  200. sb.Append(name);
  201. }
  202. sb.AppendLine();
  203. i++;
  204. }
  205. }
  206. public string DumpMethod(
  207. int maxStack,
  208. ImmutableArray<byte> ilBytes,
  209. ImmutableArray<LocalInfo> locals,
  210. IReadOnlyList<HandlerSpan> exceptionHandlers,
  211. IReadOnlyDictionary<int, string> markers = null)
  212. {
  213. var builder = new StringBuilder();
  214. this.DumpMethod(builder, maxStack, ilBytes, locals, exceptionHandlers, markers);
  215. return builder.ToString();
  216. }
  217. public void DumpMethod(
  218. StringBuilder sb,
  219. int maxStack,
  220. ImmutableArray<byte> ilBytes,
  221. ImmutableArray<LocalInfo> locals,
  222. IReadOnlyList<HandlerSpan> exceptionHandlers,
  223. IReadOnlyDictionary<int, string> markers = null)
  224. {
  225. sb.AppendLine("{");
  226. VisualizeHeader(sb, ilBytes.Length, maxStack, locals);
  227. DumpILBlock(ilBytes, ilBytes.Length, sb, exceptionHandlers, 0, markers);
  228. sb.AppendLine("}");
  229. }
  230. /// <summary>
  231. /// Dumps all instructions in the stream into provided string builder.
  232. /// The blockOffset specifies the relative position of the block within method body (if known).
  233. /// </summary>
  234. public void DumpILBlock(
  235. ImmutableArray<byte> ilBytes,
  236. int length,
  237. StringBuilder sb,
  238. IReadOnlyList<HandlerSpan> spans = null,
  239. int blockOffset = 0,
  240. IReadOnlyDictionary<int, string> markers = null)
  241. {
  242. if (ilBytes == null)
  243. {
  244. return;
  245. }
  246. int spanIndex = 0;
  247. int curIndex = DumpILBlock(ilBytes, length, sb, spans, blockOffset, 0, spanIndex, IndentString, markers, out spanIndex);
  248. //Debug.Assert(curIndex == length);
  249. //Debug.Assert(spans == null || spanIndex == spans.Count);
  250. }
  251. private int DumpILBlock(
  252. ImmutableArray<byte> ilBytes,
  253. int length,
  254. StringBuilder sb,
  255. IReadOnlyList<HandlerSpan> spans,
  256. int blockOffset,
  257. int curIndex,
  258. int spanIndex,
  259. string indent,
  260. IReadOnlyDictionary<int, string> markers,
  261. out int nextSpanIndex)
  262. {
  263. int lastSpanIndex = spanIndex - 1;
  264. while (curIndex < length)
  265. {
  266. if (lastSpanIndex > 0 && StartsFilterHandler(spans, lastSpanIndex, curIndex + blockOffset))
  267. {
  268. sb.Append(indent.Substring(0, indent.Length - IndentString.Length));
  269. sb.Append("} // end filter");
  270. sb.AppendLine();
  271. sb.Append(indent.Substring(0, indent.Length - IndentString.Length));
  272. sb.Append("{ // handler");
  273. sb.AppendLine();
  274. }
  275. if (StartsSpan(spans, spanIndex, curIndex + blockOffset))
  276. {
  277. sb.Append(indent);
  278. sb.Append(spans[spanIndex].ToString(this));
  279. sb.AppendLine();
  280. sb.Append(indent);
  281. sb.Append("{");
  282. sb.AppendLine();
  283. curIndex = DumpILBlock(ilBytes, length, sb, spans, blockOffset, curIndex, spanIndex + 1, indent + IndentString, markers, out spanIndex);
  284. sb.Append(indent);
  285. sb.Append("}");
  286. sb.AppendLine();
  287. }
  288. else
  289. {
  290. int ilOffset = curIndex + blockOffset;
  291. string marker;
  292. if (markers != null && markers.TryGetValue(ilOffset, out marker))
  293. {
  294. sb.Append(indent.Substring(0, indent.Length - marker.Length));
  295. sb.Append(marker);
  296. }
  297. else
  298. {
  299. sb.Append(indent);
  300. }
  301. sb.AppendFormat("IL_{0:x4}:", ilOffset);
  302. OpCode opCode;
  303. int expectedSize;
  304. byte op1 = ilBytes[curIndex++];
  305. if (op1 == 0xfe && curIndex < length)
  306. {
  307. byte op2 = ilBytes[curIndex++];
  308. opCode = s_twoByteOpCodes[op2];
  309. expectedSize = 2;
  310. }
  311. else
  312. {
  313. opCode = s_oneByteOpCodes[op1];
  314. expectedSize = 1;
  315. }
  316. if (opCode.Size != expectedSize)
  317. {
  318. sb.AppendLine(string.Format(" <unknown 0x{0}{1:X2}>", expectedSize == 2 ? "fe" : "", op1));
  319. continue;
  320. }
  321. sb.Append(" ");
  322. sb.AppendFormat(opCode.OperandType == OperandType.InlineNone ? "{0}" : "{0,-10}", opCode);
  323. switch (opCode.OperandType)
  324. {
  325. case OperandType.InlineField:
  326. case OperandType.InlineMethod:
  327. case OperandType.InlineTok:
  328. case OperandType.InlineType:
  329. sb.Append(' ');
  330. sb.Append(VisualizeSymbol(ReadUInt32(ilBytes, ref curIndex)));
  331. break;
  332. case OperandType.InlineSig: // signature (calli), not emitted by C#/VB
  333. sb.AppendFormat(" 0x{0:x}", ReadUInt32(ilBytes, ref curIndex));
  334. break;
  335. case OperandType.InlineString:
  336. sb.Append(' ');
  337. sb.Append(VisualizeUserString(ReadUInt32(ilBytes, ref curIndex)));
  338. break;
  339. case OperandType.InlineNone:
  340. break;
  341. case OperandType.ShortInlineI:
  342. sb.AppendFormat(" {0}", ReadSByte(ilBytes, ref curIndex));
  343. break;
  344. case OperandType.ShortInlineVar:
  345. sb.AppendFormat(" V_{0}", ReadByte(ilBytes, ref curIndex));
  346. break;
  347. case OperandType.InlineVar:
  348. sb.AppendFormat(" V_{0}", ReadUInt16(ilBytes, ref curIndex));
  349. break;
  350. case OperandType.InlineI:
  351. sb.AppendFormat(" 0x{0:x}", ReadUInt32(ilBytes, ref curIndex));
  352. break;
  353. case OperandType.InlineI8:
  354. sb.AppendFormat(" 0x{0:x8}", ReadUInt64(ilBytes, ref curIndex));
  355. break;
  356. case OperandType.ShortInlineR:
  357. {
  358. var value = ReadSingle(ilBytes, ref curIndex);
  359. if (value == 0 && 1 / value < 0)
  360. {
  361. sb.Append(" -0.0");
  362. }
  363. else
  364. {
  365. sb.AppendFormat(" {0}", value.ToString(CultureInfo.InvariantCulture));
  366. }
  367. }
  368. break;
  369. case OperandType.InlineR:
  370. {
  371. var value = ReadDouble(ilBytes, ref curIndex);
  372. if (value == 0 && 1 / value < 0)
  373. {
  374. sb.Append(" -0.0");
  375. }
  376. else
  377. {
  378. sb.AppendFormat(" {0}", value.ToString(CultureInfo.InvariantCulture));
  379. }
  380. }
  381. break;
  382. case OperandType.ShortInlineBrTarget:
  383. sb.AppendFormat(" IL_{0:x4}", ReadSByte(ilBytes, ref curIndex) + curIndex + blockOffset);
  384. break;
  385. case OperandType.InlineBrTarget:
  386. sb.AppendFormat(" IL_{0:x4}", ReadInt32(ilBytes, ref curIndex) + curIndex + blockOffset);
  387. break;
  388. case OperandType.InlineSwitch:
  389. int labelCount = ReadInt32(ilBytes, ref curIndex);
  390. int instrEnd = curIndex + labelCount * 4;
  391. sb.AppendLine("(");
  392. for (int i = 0; i < labelCount; i++)
  393. {
  394. sb.AppendFormat(" IL_{0:x4}", ReadInt32(ilBytes, ref curIndex) + instrEnd + blockOffset);
  395. sb.AppendLine((i == labelCount - 1) ? ")" : ",");
  396. }
  397. break;
  398. default:
  399. throw new InvalidOperationException();
  400. //throw ExceptionUtilities.UnexpectedValue(opCode.OperandType);
  401. }
  402. sb.AppendLine();
  403. }
  404. if (EndsSpan(spans, lastSpanIndex, curIndex + blockOffset))
  405. {
  406. break;
  407. }
  408. }
  409. nextSpanIndex = spanIndex;
  410. return curIndex;
  411. }
  412. private static bool StartsSpan(IReadOnlyList<HandlerSpan> spans, int spanIndex, int curIndex)
  413. {
  414. return spans != null && spanIndex < spans.Count && spans[spanIndex].StartOffset == (uint)curIndex;
  415. }
  416. private static bool EndsSpan(IReadOnlyList<HandlerSpan> spans, int spanIndex, int curIndex)
  417. {
  418. return spans != null && spanIndex >= 0 && spans[spanIndex].EndOffset == (uint)curIndex;
  419. }
  420. private static bool StartsFilterHandler(IReadOnlyList<HandlerSpan> spans, int spanIndex, int curIndex)
  421. {
  422. return spans != null &&
  423. spanIndex < spans.Count &&
  424. spans[spanIndex].Kind == HandlerKind.Filter &&
  425. spans[spanIndex].FilterHandlerStart == (uint)curIndex;
  426. }
  427. public static IReadOnlyList<HandlerSpan> GetHandlerSpans(ImmutableArray<ExceptionRegion> entries)
  428. {
  429. if (entries.Length == 0)
  430. {
  431. return new HandlerSpan[0];
  432. }
  433. var result = new List<HandlerSpan>();
  434. foreach (ExceptionRegion entry in entries)
  435. {
  436. int tryStartOffset = entry.TryOffset;
  437. int tryEndOffset = entry.TryOffset + entry.TryLength;
  438. var span = new HandlerSpan(HandlerKind.Try, null, tryStartOffset, tryEndOffset);
  439. if (result.Count == 0 || span.CompareTo(result[result.Count - 1]) != 0)
  440. {
  441. result.Add(span);
  442. }
  443. }
  444. foreach (ExceptionRegion entry in entries)
  445. {
  446. int handlerStartOffset = entry.HandlerOffset;
  447. int handlerEndOffset = entry.HandlerOffset + entry.HandlerLength;
  448. HandlerSpan span;
  449. switch (entry.Kind)
  450. {
  451. case ExceptionRegionKind.Catch:
  452. span = new HandlerSpan(HandlerKind.Catch, MetadataTokens.GetToken(entry.CatchType), handlerStartOffset, handlerEndOffset);
  453. break;
  454. case ExceptionRegionKind.Fault:
  455. span = new HandlerSpan(HandlerKind.Fault, null, handlerStartOffset, handlerEndOffset);
  456. break;
  457. case ExceptionRegionKind.Filter:
  458. span = new HandlerSpan(HandlerKind.Filter, null, handlerStartOffset, handlerEndOffset, entry.FilterOffset);
  459. break;
  460. case ExceptionRegionKind.Finally:
  461. span = new HandlerSpan(HandlerKind.Finally, null, handlerStartOffset, handlerEndOffset);
  462. break;
  463. default:
  464. throw new InvalidOperationException();
  465. }
  466. result.Add(span);
  467. }
  468. return result;
  469. }
  470. }
  471. }