/symbols/mdb/Mono.CompilerServices.SymbolWriter/MonoSymbolFile.cs

http://github.com/jbevain/cecil · C# · 643 lines · 472 code · 112 blank · 59 comment · 70 complexity · 33ee9607a245bdae58b527f3881f4bb7 MD5 · raw file

  1. //
  2. // MonoSymbolFile.cs
  3. //
  4. // Authors:
  5. // Martin Baulig (martin@ximian.com)
  6. // Marek Safar (marek.safar@gmail.com)
  7. //
  8. // (C) 2003 Ximian, Inc. http://www.ximian.com
  9. // Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
  10. //
  11. //
  12. // Permission is hereby granted, free of charge, to any person obtaining
  13. // a copy of this software and associated documentation files (the
  14. // "Software"), to deal in the Software without restriction, including
  15. // without limitation the rights to use, copy, modify, merge, publish,
  16. // distribute, sublicense, and/or sell copies of the Software, and to
  17. // permit persons to whom the Software is furnished to do so, subject to
  18. // the following conditions:
  19. //
  20. // The above copyright notice and this permission notice shall be
  21. // included in all copies or substantial portions of the Software.
  22. //
  23. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  24. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  27. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  28. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  29. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  30. //
  31. using System;
  32. using System.Reflection;
  33. using System.Collections.Generic;
  34. using System.IO;
  35. namespace Mono.CompilerServices.SymbolWriter
  36. {
  37. public class MonoSymbolFileException : Exception
  38. {
  39. public MonoSymbolFileException ()
  40. : base ()
  41. { }
  42. public MonoSymbolFileException (string message, params object[] args)
  43. : base (String.Format (message, args))
  44. {
  45. }
  46. public MonoSymbolFileException (string message, Exception innerException)
  47. : base (message, innerException)
  48. {
  49. }
  50. }
  51. sealed class MyBinaryWriter : BinaryWriter
  52. {
  53. public MyBinaryWriter (Stream stream)
  54. : base (stream)
  55. { }
  56. public void WriteLeb128 (int value)
  57. {
  58. base.Write7BitEncodedInt (value);
  59. }
  60. }
  61. internal class MyBinaryReader : BinaryReader
  62. {
  63. public MyBinaryReader (Stream stream)
  64. : base (stream)
  65. { }
  66. public int ReadLeb128 ()
  67. {
  68. return base.Read7BitEncodedInt ();
  69. }
  70. public string ReadString (int offset)
  71. {
  72. long old_pos = BaseStream.Position;
  73. BaseStream.Position = offset;
  74. string text = ReadString ();
  75. BaseStream.Position = old_pos;
  76. return text;
  77. }
  78. }
  79. public interface ISourceFile
  80. {
  81. SourceFileEntry Entry {
  82. get;
  83. }
  84. }
  85. public interface ICompileUnit
  86. {
  87. CompileUnitEntry Entry {
  88. get;
  89. }
  90. }
  91. public interface IMethodDef
  92. {
  93. string Name {
  94. get;
  95. }
  96. int Token {
  97. get;
  98. }
  99. }
  100. public class MonoSymbolFile : IDisposable
  101. {
  102. List<MethodEntry> methods = new List<MethodEntry> ();
  103. List<SourceFileEntry> sources = new List<SourceFileEntry> ();
  104. List<CompileUnitEntry> comp_units = new List<CompileUnitEntry> ();
  105. Dictionary<int, AnonymousScopeEntry> anonymous_scopes;
  106. OffsetTable ot;
  107. int last_type_index;
  108. int last_method_index;
  109. int last_namespace_index;
  110. public readonly int MajorVersion = OffsetTable.MajorVersion;
  111. public readonly int MinorVersion = OffsetTable.MinorVersion;
  112. public int NumLineNumbers;
  113. public MonoSymbolFile ()
  114. {
  115. ot = new OffsetTable ();
  116. }
  117. public int AddSource (SourceFileEntry source)
  118. {
  119. sources.Add (source);
  120. return sources.Count;
  121. }
  122. public int AddCompileUnit (CompileUnitEntry entry)
  123. {
  124. comp_units.Add (entry);
  125. return comp_units.Count;
  126. }
  127. public void AddMethod (MethodEntry entry)
  128. {
  129. methods.Add (entry);
  130. }
  131. public MethodEntry DefineMethod (CompileUnitEntry comp_unit, int token,
  132. ScopeVariable[] scope_vars, LocalVariableEntry[] locals,
  133. LineNumberEntry[] lines, CodeBlockEntry[] code_blocks,
  134. string real_name, MethodEntry.Flags flags,
  135. int namespace_id)
  136. {
  137. if (reader != null)
  138. throw new InvalidOperationException ();
  139. MethodEntry method = new MethodEntry (
  140. this, comp_unit, token, scope_vars, locals, lines, code_blocks,
  141. real_name, flags, namespace_id);
  142. AddMethod (method);
  143. return method;
  144. }
  145. internal void DefineAnonymousScope (int id)
  146. {
  147. if (reader != null)
  148. throw new InvalidOperationException ();
  149. if (anonymous_scopes == null)
  150. anonymous_scopes = new Dictionary<int, AnonymousScopeEntry> ();
  151. anonymous_scopes.Add (id, new AnonymousScopeEntry (id));
  152. }
  153. internal void DefineCapturedVariable (int scope_id, string name, string captured_name,
  154. CapturedVariable.CapturedKind kind)
  155. {
  156. if (reader != null)
  157. throw new InvalidOperationException ();
  158. AnonymousScopeEntry scope = anonymous_scopes [scope_id];
  159. scope.AddCapturedVariable (name, captured_name, kind);
  160. }
  161. internal void DefineCapturedScope (int scope_id, int id, string captured_name)
  162. {
  163. if (reader != null)
  164. throw new InvalidOperationException ();
  165. AnonymousScopeEntry scope = anonymous_scopes [scope_id];
  166. scope.AddCapturedScope (id, captured_name);
  167. }
  168. internal int GetNextTypeIndex ()
  169. {
  170. return ++last_type_index;
  171. }
  172. internal int GetNextMethodIndex ()
  173. {
  174. return ++last_method_index;
  175. }
  176. internal int GetNextNamespaceIndex ()
  177. {
  178. return ++last_namespace_index;
  179. }
  180. void Write (MyBinaryWriter bw, Guid guid)
  181. {
  182. // Magic number and file version.
  183. bw.Write (OffsetTable.Magic);
  184. bw.Write (MajorVersion);
  185. bw.Write (MinorVersion);
  186. bw.Write (guid.ToByteArray ());
  187. //
  188. // Offsets of file sections; we must write this after we're done
  189. // writing the whole file, so we just reserve the space for it here.
  190. //
  191. long offset_table_offset = bw.BaseStream.Position;
  192. ot.Write (bw, MajorVersion, MinorVersion);
  193. //
  194. // Sort the methods according to their tokens and update their index.
  195. //
  196. methods.Sort ();
  197. for (int i = 0; i < methods.Count; i++)
  198. methods [i].Index = i + 1;
  199. //
  200. // Write data sections.
  201. //
  202. ot.DataSectionOffset = (int) bw.BaseStream.Position;
  203. foreach (SourceFileEntry source in sources)
  204. source.WriteData (bw);
  205. foreach (CompileUnitEntry comp_unit in comp_units)
  206. comp_unit.WriteData (bw);
  207. foreach (MethodEntry method in methods)
  208. method.WriteData (this, bw);
  209. ot.DataSectionSize = (int) bw.BaseStream.Position - ot.DataSectionOffset;
  210. //
  211. // Write the method index table.
  212. //
  213. ot.MethodTableOffset = (int) bw.BaseStream.Position;
  214. for (int i = 0; i < methods.Count; i++) {
  215. MethodEntry entry = methods [i];
  216. entry.Write (bw);
  217. }
  218. ot.MethodTableSize = (int) bw.BaseStream.Position - ot.MethodTableOffset;
  219. //
  220. // Write source table.
  221. //
  222. ot.SourceTableOffset = (int) bw.BaseStream.Position;
  223. for (int i = 0; i < sources.Count; i++) {
  224. SourceFileEntry source = sources [i];
  225. source.Write (bw);
  226. }
  227. ot.SourceTableSize = (int) bw.BaseStream.Position - ot.SourceTableOffset;
  228. //
  229. // Write compilation unit table.
  230. //
  231. ot.CompileUnitTableOffset = (int) bw.BaseStream.Position;
  232. for (int i = 0; i < comp_units.Count; i++) {
  233. CompileUnitEntry unit = comp_units [i];
  234. unit.Write (bw);
  235. }
  236. ot.CompileUnitTableSize = (int) bw.BaseStream.Position - ot.CompileUnitTableOffset;
  237. //
  238. // Write anonymous scope table.
  239. //
  240. ot.AnonymousScopeCount = anonymous_scopes != null ? anonymous_scopes.Count : 0;
  241. ot.AnonymousScopeTableOffset = (int) bw.BaseStream.Position;
  242. if (anonymous_scopes != null) {
  243. foreach (AnonymousScopeEntry scope in anonymous_scopes.Values)
  244. scope.Write (bw);
  245. }
  246. ot.AnonymousScopeTableSize = (int) bw.BaseStream.Position - ot.AnonymousScopeTableOffset;
  247. //
  248. // Fixup offset table.
  249. //
  250. ot.TypeCount = last_type_index;
  251. ot.MethodCount = methods.Count;
  252. ot.SourceCount = sources.Count;
  253. ot.CompileUnitCount = comp_units.Count;
  254. //
  255. // Write offset table.
  256. //
  257. ot.TotalFileSize = (int) bw.BaseStream.Position;
  258. bw.Seek ((int) offset_table_offset, SeekOrigin.Begin);
  259. ot.Write (bw, MajorVersion, MinorVersion);
  260. bw.Seek (0, SeekOrigin.End);
  261. #if false
  262. Console.WriteLine ("TOTAL: {0} line numbes, {1} bytes, extended {2} bytes, " +
  263. "{3} methods.", NumLineNumbers, LineNumberSize,
  264. ExtendedLineNumberSize, methods.Count);
  265. #endif
  266. }
  267. public void CreateSymbolFile (Guid guid, FileStream fs)
  268. {
  269. if (reader != null)
  270. throw new InvalidOperationException ();
  271. Write (new MyBinaryWriter (fs), guid);
  272. }
  273. MyBinaryReader reader;
  274. Dictionary<int, SourceFileEntry> source_file_hash;
  275. Dictionary<int, CompileUnitEntry> compile_unit_hash;
  276. List<MethodEntry> method_list;
  277. Dictionary<int, MethodEntry> method_token_hash;
  278. Dictionary<string, int> source_name_hash;
  279. Guid guid;
  280. MonoSymbolFile (Stream stream)
  281. {
  282. reader = new MyBinaryReader (stream);
  283. try {
  284. long magic = reader.ReadInt64 ();
  285. int major_version = reader.ReadInt32 ();
  286. int minor_version = reader.ReadInt32 ();
  287. if (magic != OffsetTable.Magic)
  288. throw new MonoSymbolFileException ("Symbol file is not a valid");
  289. if (major_version != OffsetTable.MajorVersion)
  290. throw new MonoSymbolFileException (
  291. "Symbol file has version {0} but expected {1}", major_version, OffsetTable.MajorVersion);
  292. if (minor_version != OffsetTable.MinorVersion)
  293. throw new MonoSymbolFileException ("Symbol file has version {0}.{1} but expected {2}.{3}",
  294. major_version, minor_version,
  295. OffsetTable.MajorVersion, OffsetTable.MinorVersion);
  296. MajorVersion = major_version;
  297. MinorVersion = minor_version;
  298. guid = new Guid (reader.ReadBytes (16));
  299. ot = new OffsetTable (reader, major_version, minor_version);
  300. } catch (Exception e) {
  301. throw new MonoSymbolFileException ("Cannot read symbol file", e);
  302. }
  303. source_file_hash = new Dictionary<int, SourceFileEntry> ();
  304. compile_unit_hash = new Dictionary<int, CompileUnitEntry> ();
  305. }
  306. #if !NET_CORE
  307. public static MonoSymbolFile ReadSymbolFile (Assembly assembly)
  308. {
  309. string filename = assembly.Location;
  310. string name = filename + ".mdb";
  311. Module[] modules = assembly.GetModules ();
  312. Guid assembly_guid = modules[0].ModuleVersionId;
  313. return ReadSymbolFile (name, assembly_guid);
  314. }
  315. #endif
  316. public static MonoSymbolFile ReadSymbolFile (string mdbFilename)
  317. {
  318. return ReadSymbolFile (new FileStream (mdbFilename, FileMode.Open, FileAccess.Read));
  319. }
  320. public static MonoSymbolFile ReadSymbolFile (string mdbFilename, Guid assemblyGuid)
  321. {
  322. var sf = ReadSymbolFile (mdbFilename);
  323. if (assemblyGuid != sf.guid)
  324. throw new MonoSymbolFileException ("Symbol file `{0}' does not match assembly", mdbFilename);
  325. return sf;
  326. }
  327. public static MonoSymbolFile ReadSymbolFile (Stream stream)
  328. {
  329. return new MonoSymbolFile (stream);
  330. }
  331. public int CompileUnitCount {
  332. get { return ot.CompileUnitCount; }
  333. }
  334. public int SourceCount {
  335. get { return ot.SourceCount; }
  336. }
  337. public int MethodCount {
  338. get { return ot.MethodCount; }
  339. }
  340. public int TypeCount {
  341. get { return ot.TypeCount; }
  342. }
  343. public int AnonymousScopeCount {
  344. get { return ot.AnonymousScopeCount; }
  345. }
  346. public int NamespaceCount {
  347. get { return last_namespace_index; }
  348. }
  349. public Guid Guid {
  350. get { return guid; }
  351. }
  352. public OffsetTable OffsetTable {
  353. get { return ot; }
  354. }
  355. internal int LineNumberCount = 0;
  356. internal int LocalCount = 0;
  357. internal int StringSize = 0;
  358. internal int LineNumberSize = 0;
  359. internal int ExtendedLineNumberSize = 0;
  360. public SourceFileEntry GetSourceFile (int index)
  361. {
  362. if ((index < 1) || (index > ot.SourceCount))
  363. throw new ArgumentException ();
  364. if (reader == null)
  365. throw new InvalidOperationException ();
  366. lock (this) {
  367. SourceFileEntry source;
  368. if (source_file_hash.TryGetValue (index, out source))
  369. return source;
  370. long old_pos = reader.BaseStream.Position;
  371. reader.BaseStream.Position = ot.SourceTableOffset +
  372. SourceFileEntry.Size * (index - 1);
  373. source = new SourceFileEntry (this, reader);
  374. source_file_hash.Add (index, source);
  375. reader.BaseStream.Position = old_pos;
  376. return source;
  377. }
  378. }
  379. public SourceFileEntry[] Sources {
  380. get {
  381. if (reader == null)
  382. throw new InvalidOperationException ();
  383. SourceFileEntry[] retval = new SourceFileEntry [SourceCount];
  384. for (int i = 0; i < SourceCount; i++)
  385. retval [i] = GetSourceFile (i + 1);
  386. return retval;
  387. }
  388. }
  389. public CompileUnitEntry GetCompileUnit (int index)
  390. {
  391. if ((index < 1) || (index > ot.CompileUnitCount))
  392. throw new ArgumentException ();
  393. if (reader == null)
  394. throw new InvalidOperationException ();
  395. lock (this) {
  396. CompileUnitEntry unit;
  397. if (compile_unit_hash.TryGetValue (index, out unit))
  398. return unit;
  399. long old_pos = reader.BaseStream.Position;
  400. reader.BaseStream.Position = ot.CompileUnitTableOffset +
  401. CompileUnitEntry.Size * (index - 1);
  402. unit = new CompileUnitEntry (this, reader);
  403. compile_unit_hash.Add (index, unit);
  404. reader.BaseStream.Position = old_pos;
  405. return unit;
  406. }
  407. }
  408. public CompileUnitEntry[] CompileUnits {
  409. get {
  410. if (reader == null)
  411. throw new InvalidOperationException ();
  412. CompileUnitEntry[] retval = new CompileUnitEntry [CompileUnitCount];
  413. for (int i = 0; i < CompileUnitCount; i++)
  414. retval [i] = GetCompileUnit (i + 1);
  415. return retval;
  416. }
  417. }
  418. void read_methods ()
  419. {
  420. lock (this) {
  421. if (method_token_hash != null)
  422. return;
  423. method_token_hash = new Dictionary<int, MethodEntry> ();
  424. method_list = new List<MethodEntry> ();
  425. long old_pos = reader.BaseStream.Position;
  426. reader.BaseStream.Position = ot.MethodTableOffset;
  427. for (int i = 0; i < MethodCount; i++) {
  428. MethodEntry entry = new MethodEntry (this, reader, i + 1);
  429. method_token_hash.Add (entry.Token, entry);
  430. method_list.Add (entry);
  431. }
  432. reader.BaseStream.Position = old_pos;
  433. }
  434. }
  435. public MethodEntry GetMethodByToken (int token)
  436. {
  437. if (reader == null)
  438. throw new InvalidOperationException ();
  439. lock (this) {
  440. read_methods ();
  441. MethodEntry me;
  442. method_token_hash.TryGetValue (token, out me);
  443. return me;
  444. }
  445. }
  446. public MethodEntry GetMethod (int index)
  447. {
  448. if ((index < 1) || (index > ot.MethodCount))
  449. throw new ArgumentException ();
  450. if (reader == null)
  451. throw new InvalidOperationException ();
  452. lock (this) {
  453. read_methods ();
  454. return method_list [index - 1];
  455. }
  456. }
  457. public MethodEntry[] Methods {
  458. get {
  459. if (reader == null)
  460. throw new InvalidOperationException ();
  461. lock (this) {
  462. read_methods ();
  463. MethodEntry[] retval = new MethodEntry [MethodCount];
  464. method_list.CopyTo (retval, 0);
  465. return retval;
  466. }
  467. }
  468. }
  469. public int FindSource (string file_name)
  470. {
  471. if (reader == null)
  472. throw new InvalidOperationException ();
  473. lock (this) {
  474. if (source_name_hash == null) {
  475. source_name_hash = new Dictionary<string, int> ();
  476. for (int i = 0; i < ot.SourceCount; i++) {
  477. SourceFileEntry source = GetSourceFile (i + 1);
  478. source_name_hash.Add (source.FileName, i);
  479. }
  480. }
  481. int value;
  482. if (!source_name_hash.TryGetValue (file_name, out value))
  483. return -1;
  484. return value;
  485. }
  486. }
  487. public AnonymousScopeEntry GetAnonymousScope (int id)
  488. {
  489. if (reader == null)
  490. throw new InvalidOperationException ();
  491. AnonymousScopeEntry scope;
  492. lock (this) {
  493. if (anonymous_scopes != null) {
  494. anonymous_scopes.TryGetValue (id, out scope);
  495. return scope;
  496. }
  497. anonymous_scopes = new Dictionary<int, AnonymousScopeEntry> ();
  498. reader.BaseStream.Position = ot.AnonymousScopeTableOffset;
  499. for (int i = 0; i < ot.AnonymousScopeCount; i++) {
  500. scope = new AnonymousScopeEntry (reader);
  501. anonymous_scopes.Add (scope.ID, scope);
  502. }
  503. return anonymous_scopes [id];
  504. }
  505. }
  506. internal MyBinaryReader BinaryReader {
  507. get {
  508. if (reader == null)
  509. throw new InvalidOperationException ();
  510. return reader;
  511. }
  512. }
  513. public void Dispose ()
  514. {
  515. Dispose (true);
  516. }
  517. protected virtual void Dispose (bool disposing)
  518. {
  519. if (disposing) {
  520. if (reader != null) {
  521. #if NET_CORE
  522. reader.Dispose ();
  523. #else
  524. reader.Close ();
  525. #endif
  526. reader = null;
  527. }
  528. }
  529. }
  530. }
  531. }