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