1 //
2 // MdbWriter.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@gmail.com)
6 //
7 // Copyright (c) 2008 - 2011 Jb Evain
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 
29 using System;
30 using System.Collections.Generic;
31 using System.IO;
32 
33 using Mono.Cecil.Cil;
34 using Mono.Collections.Generic;
35 using Mono.CompilerServices.SymbolWriter;
36 
37 namespace Mono.Cecil.Mdb {
38 
39 #if !READ_ONLY
40 	public class MdbWriterProvider : ISymbolWriterProvider {
41 
GetSymbolWriter(ModuleDefinition module, string fileName)42 		public ISymbolWriter GetSymbolWriter (ModuleDefinition module, string fileName)
43 		{
44 			return new MdbWriter (module.Mvid, fileName);
45 		}
46 
GetSymbolWriter(ModuleDefinition module, Stream symbolStream)47 		public ISymbolWriter GetSymbolWriter (ModuleDefinition module, Stream symbolStream)
48 		{
49 			throw new NotImplementedException ();
50 		}
51 	}
52 
53 	public class MdbWriter : ISymbolWriter {
54 
55 		readonly Guid mvid;
56 		readonly MonoSymbolWriter writer;
57 		readonly Dictionary<string, SourceFile> source_files;
58 
MdbWriter(Guid mvid, string assembly)59 		public MdbWriter (Guid mvid, string assembly)
60 		{
61 			this.mvid = mvid;
62 			this.writer = new MonoSymbolWriter (assembly);
63 			this.source_files = new Dictionary<string, SourceFile> ();
64 		}
65 
GetInstructions(MethodBody body)66 		static Collection<Instruction> GetInstructions (MethodBody body)
67 		{
68 			var instructions = new Collection<Instruction> ();
69 			foreach (var instruction in body.Instructions)
70 				if (instruction.SequencePoint != null)
71 					instructions.Add (instruction);
72 
73 			return instructions;
74 		}
75 
GetSourceFile(Document document)76 		SourceFile GetSourceFile (Document document)
77 		{
78 			var url = document.Url;
79 
80 			SourceFile source_file;
81 			if (source_files.TryGetValue (url, out source_file))
82 				return source_file;
83 
84 			var entry = writer.DefineDocument (url);
85 			var compile_unit = writer.DefineCompilationUnit (entry);
86 
87 			source_file = new SourceFile (compile_unit, entry);
88 			source_files.Add (url, source_file);
89 			return source_file;
90 		}
91 
Populate(Collection<Instruction> instructions, int [] offsets, int [] startRows, int [] startCols, out SourceFile file)92 		void Populate (Collection<Instruction> instructions, int [] offsets,
93 			int [] startRows, int [] startCols, out SourceFile file)
94 		{
95 			SourceFile source_file = null;
96 
97 			for (int i = 0; i < instructions.Count; i++) {
98 				var instruction = instructions [i];
99 				offsets [i] = instruction.Offset;
100 
101 				var sequence_point = instruction.SequencePoint;
102 				if (source_file == null)
103 					source_file = GetSourceFile (sequence_point.Document);
104 
105 				startRows [i] = sequence_point.StartLine;
106 				startCols [i] = sequence_point.StartColumn;
107 			}
108 
109 			file = source_file;
110 		}
111 
Write(MethodBody body)112 		public void Write (MethodBody body)
113 		{
114 			var method = new SourceMethod (body.Method);
115 
116 			var instructions = GetInstructions (body);
117 			int count = instructions.Count;
118 			if (count == 0)
119 				return;
120 
121 			var offsets = new int [count];
122 			var start_rows = new int [count];
123 			var start_cols = new int [count];
124 
125 			SourceFile file;
126 			Populate (instructions, offsets, start_rows, start_cols, out file);
127 
128 			var builder = writer.OpenMethod (file.CompilationUnit, 0, method);
129 
130 			for (int i = 0; i < count; i++)
131 				builder.MarkSequencePoint (
132 					offsets [i],
133 					file.CompilationUnit.SourceFile,
134 					start_rows [i],
135 					start_cols [i],
136 					false);
137 
138 			if (body.HasVariables)
139 				AddVariables (body.Variables);
140 
141 			writer.CloseMethod ();
142 		}
143 
144 		readonly static byte [] empty_header = new byte [0];
145 
GetDebugHeader(out ImageDebugDirectory directory, out byte [] header)146 		public bool GetDebugHeader (out ImageDebugDirectory directory, out byte [] header)
147 		{
148 			directory = new ImageDebugDirectory ();
149 			header = empty_header;
150 			return false;
151 		}
152 
AddVariables(IList<VariableDefinition> variables)153 		void AddVariables (IList<VariableDefinition> variables)
154 		{
155 			for (int i = 0; i < variables.Count; i++) {
156 				var variable = variables [i];
157 				writer.DefineLocalVariable (i, variable.Name);
158 			}
159 		}
160 
Write(MethodSymbols symbols)161 		public void Write (MethodSymbols symbols)
162 		{
163 			var method = new SourceMethodSymbol (symbols);
164 
165 			var file = GetSourceFile (symbols.Instructions [0].SequencePoint.Document);
166 			var builder = writer.OpenMethod (file.CompilationUnit, 0, method);
167 			var count = symbols.Instructions.Count;
168 
169 			for (int i = 0; i < count; i++) {
170 				var instruction = symbols.Instructions [i];
171 				var sequence_point = instruction.SequencePoint;
172 
173 				builder.MarkSequencePoint (
174 					instruction.Offset,
175 					GetSourceFile (sequence_point.Document).CompilationUnit.SourceFile,
176 					sequence_point.StartLine,
177 					sequence_point.EndLine,
178 					false);
179 			}
180 
181 			if (symbols.HasVariables)
182 				AddVariables (symbols.Variables);
183 
184 			writer.CloseMethod ();
185 		}
186 
Dispose()187 		public void Dispose ()
188 		{
189 			writer.WriteSymbolFile (mvid);
190 		}
191 
192 		class SourceFile : ISourceFile {
193 
194 			readonly CompileUnitEntry compilation_unit;
195 			readonly SourceFileEntry entry;
196 
197 			public SourceFileEntry Entry {
198 				get { return entry; }
199 			}
200 
201 			public CompileUnitEntry CompilationUnit {
202 				get { return compilation_unit; }
203 			}
204 
SourceFile(CompileUnitEntry comp_unit, SourceFileEntry entry)205 			public SourceFile (CompileUnitEntry comp_unit, SourceFileEntry entry)
206 			{
207 				this.compilation_unit = comp_unit;
208 				this.entry = entry;
209 			}
210 		}
211 
212 		class SourceMethodSymbol : IMethodDef {
213 
214 			readonly string name;
215 			readonly int token;
216 
217 			public string Name {
218 				get { return name;}
219 			}
220 
221 			public int Token {
222 				get { return token; }
223 			}
224 
SourceMethodSymbol(MethodSymbols symbols)225 			public SourceMethodSymbol (MethodSymbols symbols)
226 			{
227 				name = symbols.MethodName;
228 				token = symbols.MethodToken.ToInt32 ();
229 			}
230 		}
231 
232 		class SourceMethod : IMethodDef {
233 
234 			readonly MethodDefinition method;
235 
236 			public string Name {
237 				get { return method.Name; }
238 			}
239 
240 			public int Token {
241 				get { return method.MetadataToken.ToInt32 (); }
242 			}
243 
SourceMethod(MethodDefinition method)244 			public SourceMethod (MethodDefinition method)
245 			{
246 				this.method = method;
247 			}
248 		}
249 	}
250 #endif
251 }
252