2 // SourceMethodBuilder.cs
5 // Martin Baulig (martin@ximian.com)
6 // Marek Safar (marek.safar@gmail.com)
8 // (C) 2002 Ximian, Inc. http://www.ximian.com
9 // Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections.Generic;
34 namespace Mono.CompilerServices.SymbolWriter
36 public class SourceMethodBuilder
38 List<LocalVariableEntry> _locals;
39 List<CodeBlockEntry> _blocks;
40 List<ScopeVariable> _scope_vars;
41 Stack<CodeBlockEntry> _block_stack;
42 readonly List<LineNumberEntry> method_lines;
44 readonly ICompileUnit _comp_unit;
46 readonly IMethodDef method;
48 public SourceMethodBuilder (ICompileUnit comp_unit)
50 this._comp_unit = comp_unit;
51 method_lines = new List<LineNumberEntry> ();
54 public SourceMethodBuilder (ICompileUnit comp_unit, int ns_id, IMethodDef method)
61 public void MarkSequencePoint (int offset, SourceFileEntry file, int line, int column, bool is_hidden)
63 MarkSequencePoint (offset, file, line, column, -1, -1, is_hidden);
66 public void MarkSequencePoint (int offset, SourceFileEntry file, int line, int column, int end_line, int end_column, bool is_hidden)
68 int file_idx = file != null ? file.Index : 0;
69 var lne = new LineNumberEntry (file_idx, line, column, end_line, end_column, offset, is_hidden);
71 if (method_lines.Count > 0) {
72 var prev = method_lines[method_lines.Count - 1];
75 // Same offset cannot be used for multiple lines
77 if (prev.Offset == offset) {
79 // Use the new location because debugger will adjust
80 // the breakpoint to next line with sequence point
82 if (LineNumberEntry.LocationComparer.Default.Compare (lne, prev) > 0)
83 method_lines[method_lines.Count - 1] = lne;
89 method_lines.Add (lne);
92 public void StartBlock (CodeBlockEntry.Type type, int start_offset)
94 StartBlock (type, start_offset, _blocks == null ? 1 : _blocks.Count + 1);
97 public void StartBlock (CodeBlockEntry.Type type, int start_offset, int scopeIndex)
99 if (_block_stack == null) {
100 _block_stack = new Stack<CodeBlockEntry> ();
104 _blocks = new List<CodeBlockEntry> ();
106 int parent = CurrentBlock != null ? CurrentBlock.Index : -1;
108 CodeBlockEntry block = new CodeBlockEntry (
109 scopeIndex, parent, type, start_offset);
111 _block_stack.Push (block);
115 public void EndBlock (int end_offset)
117 CodeBlockEntry block = (CodeBlockEntry) _block_stack.Pop ();
118 block.Close (end_offset);
121 public CodeBlockEntry[] Blocks {
124 return new CodeBlockEntry [0];
126 CodeBlockEntry[] retval = new CodeBlockEntry [_blocks.Count];
127 _blocks.CopyTo (retval, 0);
132 public CodeBlockEntry CurrentBlock {
134 if ((_block_stack != null) && (_block_stack.Count > 0))
135 return (CodeBlockEntry) _block_stack.Peek ();
141 public LocalVariableEntry[] Locals {
144 return new LocalVariableEntry [0];
146 return _locals.ToArray ();
151 public ICompileUnit SourceFile {
157 public void AddLocal (int index, string name)
160 _locals = new List<LocalVariableEntry> ();
161 int block_idx = CurrentBlock != null ? CurrentBlock.Index : 0;
162 _locals.Add (new LocalVariableEntry (index, name, block_idx));
165 public ScopeVariable[] ScopeVariables {
167 if (_scope_vars == null)
168 return new ScopeVariable [0];
170 return _scope_vars.ToArray ();
174 public void AddScopeVariable (int scope, int index)
176 if (_scope_vars == null)
177 _scope_vars = new List<ScopeVariable> ();
179 new ScopeVariable (scope, index));
182 public void DefineMethod (MonoSymbolFile file)
184 DefineMethod (file, method.Token);
187 public void DefineMethod (MonoSymbolFile file, int token)
190 if (blocks.Length > 0) {
192 // When index is provided by user it can be inserted in
193 // any order but mdb format does not store its value. It
194 // uses stored order as the index instead.
196 var sorted = new List<CodeBlockEntry> (blocks.Length);
198 for (int i = 0; i < blocks.Length; ++i) {
199 max_index = System.Math.Max (max_index, blocks [i].Index);
202 for (int i = 0; i < max_index; ++i) {
203 var scope_index = i + 1;
208 if (i < blocks.Length && blocks [i].Index == scope_index) {
209 sorted.Add (blocks [i]);
214 for (int ii = 0; ii < blocks.Length; ++ii) {
215 if (blocks [ii].Index == scope_index) {
216 sorted.Add (blocks [ii]);
226 // Ideally this should never happen but with current design we can
227 // generate scope index for unreachable code before reachable code
229 sorted.Add (new CodeBlockEntry (scope_index, -1, CodeBlockEntry.Type.CompilerGenerated, 0));
232 blocks = sorted.ToArray ();
233 for (int i = 0; i < blocks.Length; ++i) {
234 if (blocks [i].Index - 1 != i)
235 throw new ArgumentException ("CodeBlocks cannot be converted to mdb format");
239 var entry = new MethodEntry (
240 file, _comp_unit.Entry, token, ScopeVariables,
241 Locals, method_lines.ToArray (), blocks, null, MethodEntry.Flags.ColumnsInfoIncluded, ns_id);
243 file.AddMethod (entry);