Merge pull request #347 from JamesB7/master
[mono.git] / mcs / class / Mono.Debugger.Soft / Mono.Debugger.Soft / MethodBodyMirror.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using Mono.Cecil.Cil;
5 using Mono.Cecil.Metadata;
6 using System.IO;
7 using System.Linq;
8 using System.Reflection;
9
10 namespace Mono.Debugger.Soft
11 {
12         public class MethodBodyMirror : Mirror
13         {
14                 MethodMirror method;
15                 MethodBodyInfo info;
16
17                 internal MethodBodyMirror (VirtualMachine vm, MethodMirror method, MethodBodyInfo info) : base (vm, 0) {
18                         this.method = method;
19                         this.info = info;
20                 }
21
22                 public MethodMirror Method {
23                         get {
24                                 return method;
25                         }
26                 }
27
28                 public List<ILExceptionHandler> ExceptionHandlers {
29                         get {
30                                 vm.CheckProtocolVersion (2, 18);
31                                 return info.clauses.Select (c =>
32                                 {
33                                         var handler = new ILExceptionHandler (c.try_offset, c.try_length, (ILExceptionHandlerType) c.flags, c.handler_offset, c.handler_length);
34                                         if (c.flags == ExceptionClauseFlags.None)
35                                                 handler.CatchType = vm.GetType (c.catch_type_id);
36                                         else if (c.flags == ExceptionClauseFlags.Filter)
37                                                 handler.FilterOffset = c.filter_offset;
38
39                                         return handler;
40                                 }).ToList ();
41                         }
42                 }
43
44                 public byte[] GetILAsByteArray () {
45                         return info.il;
46                 }
47
48                 public List<ILInstruction> Instructions {
49                         get {
50                                 return ReadCilBody (new BinaryReader (new MemoryStream (info.il)), info.il.Length);
51                         }
52                 }
53
54                 static bool opcodes_inited;
55
56                 static OpCode [] OneByteOpCode = new OpCode [0xe0 + 1];
57                 static OpCode [] TwoBytesOpCode = new OpCode [0x1e + 1];
58
59                 // Adapted from Cecil
60             List<ILInstruction> ReadCilBody (BinaryReader br, int code_size)
61                 {
62                         long start = br.BaseStream.Position;
63                         ILInstruction last = null;
64                         //GenericContext context = new GenericContext (body.Method);
65                         List<ILInstruction> code = new List<ILInstruction> ();
66
67                         var by_offset = new Dictionary<int, ILInstruction> ();
68
69                         if (!opcodes_inited) {
70                                 foreach (FieldInfo fi in typeof (OpCodes).GetFields (BindingFlags.Static|BindingFlags.Public)) {
71                                         var val = (OpCode)fi.GetValue (null);
72
73                                         if (val.Op1 == 0xff)
74                                                 OneByteOpCode [val.Op2] = val;
75                                         else
76                                                 TwoBytesOpCode [val.Op2] = val;
77                                 }
78                                 opcodes_inited = true;
79                         }
80
81                         while (br.BaseStream.Position < start + code_size) {
82                                 OpCode op;
83                                 long offset = br.BaseStream.Position - start;
84                                 int cursor = br.ReadByte ();
85                                 int token;
86                                 ResolvedToken t;
87
88                                 if (cursor == 0xfe)
89                                         op = TwoBytesOpCode [br.ReadByte ()];
90                                 else
91                                         op = OneByteOpCode [cursor];
92
93                                 ILInstruction instr = new ILInstruction ((int)offset, op, null);
94
95                                 by_offset [instr.Offset] = instr;
96
97                                 switch (op.OperandType) {
98                                 case OperandType.InlineNone :
99                                         break;
100                                 case OperandType.InlineSwitch :
101                                         uint length = br.ReadUInt32 ();
102                                         int [] branches = new int [length];
103                                         int [] buf = new int [length];
104                                         for (int i = 0; i < length; i++)
105                                                 buf [i] = br.ReadInt32 ();
106                                         for (int i = 0; i < length; i++)
107                                                 branches [i] = Convert.ToInt32 (br.BaseStream.Position - start + buf [i]);
108                                         instr.Operand = branches;
109                                         break;
110                                 case OperandType.ShortInlineBrTarget :
111                                         sbyte sbrtgt = br.ReadSByte ();
112                                         instr.Operand = Convert.ToInt32 (br.BaseStream.Position - start + sbrtgt);
113                                         break;
114                                 case OperandType.InlineBrTarget :
115                                         int brtgt = br.ReadInt32 ();
116                                         instr.Operand = Convert.ToInt32 (br.BaseStream.Position - start + brtgt);
117                                         break;
118                                 case OperandType.ShortInlineI :
119                                         if (op == OpCodes.Ldc_I4_S)
120                                                 instr.Operand = br.ReadSByte ();
121                                         else
122                                                 instr.Operand = br.ReadByte ();
123                                         break;
124                                 case OperandType.ShortInlineVar :
125                                         instr.Operand = br.ReadByte ();
126                                         break;
127                                 case OperandType.ShortInlineArg :
128                                         instr.Operand = br.ReadByte ();
129                                         break;
130                                 case OperandType.InlineSig :
131                                         br.ReadInt32 ();
132                                         //instr.Operand = GetCallSiteAt (br.ReadInt32 (), context);
133                                         break;
134                                 case OperandType.InlineI :
135                                         instr.Operand = br.ReadInt32 ();
136                                         break;
137                                 case OperandType.InlineVar :
138                                         instr.Operand = br.ReadInt16 ();
139                                         break;
140                                 case OperandType.InlineArg :
141                                         instr.Operand = br.ReadInt16 ();
142                                         break;
143                                 case OperandType.InlineI8 :
144                                         instr.Operand = br.ReadInt64 ();
145                                         break;
146                                 case OperandType.ShortInlineR :
147                                         instr.Operand = br.ReadSingle ();
148                                         break;
149                                 case OperandType.InlineR :
150                                         instr.Operand = br.ReadDouble ();
151                                         break;
152                                 case OperandType.InlineString :
153                                         token = br.ReadInt32 ();
154                                         t = vm.conn.Method_ResolveToken (Method.Id, token);
155                                         if (t.Type == TokenType.STRING)
156                                                 instr.Operand = t.Str;
157                                         break;
158                                 case OperandType.InlineField :
159                                 case OperandType.InlineMethod :
160                                 case OperandType.InlineType :
161                                 case OperandType.InlineTok :
162                                         token = br.ReadInt32 ();
163
164                                         t = vm.conn.Method_ResolveToken (Method.Id, token);
165
166                                         switch (t.Type) {
167                                         case TokenType.TYPE:
168                                                 instr.Operand = vm.GetType (t.Id);
169                                                 break;
170                                         case TokenType.FIELD:
171                                                 // FIXME: No vm.GetField ()
172                                                 //instr.Operand = vm.GetField (t.Id);
173                                                 break;
174                                         case TokenType.METHOD:
175                                                 instr.Operand = vm.GetMethod (t.Id);
176                                                 break;
177                                         case TokenType.UNKNOWN:
178                                                 break;
179                                         default:
180                                                 throw new NotImplementedException ("Unknown token type: " + t.Type);
181                                         }
182                                         break;
183                                 }
184
185                                 if (last != null) {
186                                         last.Next = instr;
187                                         instr.Previous = last;
188                                 }
189
190                                 last = instr;
191
192                                 code.Add (instr);
193                         }
194
195                         // resolve branches
196                         foreach (ILInstruction i in code) {
197                                 switch (i.OpCode.OperandType) {
198                                 case OperandType.ShortInlineBrTarget:
199                                 case OperandType.InlineBrTarget:
200                                         i.Operand = by_offset [(int)i.Operand];
201                                         break;
202                                 case OperandType.InlineSwitch:
203                                         int [] lbls = (int []) i.Operand;
204                                         ILInstruction [] instrs = new ILInstruction [lbls.Length];
205                                         for (int j = 0; j < lbls.Length; j++)
206                                                 instrs [j] = by_offset [lbls [j]];
207                                         i.Operand = instrs;
208                                         break;
209                                 }
210                         }
211
212                         return code;
213                 }
214         }
215 }