Merge pull request #799 from kebby/master
[mono.git] / mcs / tools / cil-strip / Mono.Cecil.Cil / CodeReader.cs
1 //
2 // CodeReader.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@gmail.com)
6 //
7 // (C) 2005 - 2007 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 namespace Mono.Cecil.Cil {
30
31         using System;
32         using System.Collections;
33         using System.IO;
34
35         using Mono.Cecil;
36         using Mono.Cecil.Metadata;
37         using Mono.Cecil.Signatures;
38
39         sealed class CodeReader : BaseCodeVisitor {
40
41                 ReflectionReader m_reflectReader;
42                 MetadataRoot m_root;
43                 IDictionary m_instructions;
44
45                 public CodeReader (ReflectionReader reflectReader)
46                 {
47                         m_reflectReader = reflectReader;
48                         m_root = m_reflectReader.MetadataRoot;
49                         m_instructions = new Hashtable ();
50                 }
51
52                 public override void VisitMethodBody (MethodBody body)
53                 {
54                         MethodDefinition meth = body.Method;
55                         MethodBody methBody = body;
56                         BinaryReader br = m_reflectReader.Module.ImageReader.MetadataReader.GetDataReader (meth.RVA);
57
58                         // lets read the method
59                         int flags = br.ReadByte ();
60                         switch (flags & 0x3) {
61                         case (int) MethodHeader.TinyFormat :
62                                 methBody.CodeSize = flags >> 2;
63                                 methBody.MaxStack = 8;
64                                 ReadCilBody (methBody, br);
65                                 break;
66                         case (int) MethodHeader.FatFormat :
67                                 br.BaseStream.Position--;
68                                 int fatflags = br.ReadUInt16 ();
69                                 //int headersize = (fatflags >> 12) & 0xf;
70                                 methBody.MaxStack = br.ReadUInt16 ();
71                                 methBody.CodeSize = br.ReadInt32 ();
72                                 methBody.LocalVarToken = br.ReadInt32 ();
73                                 body.InitLocals = (fatflags & (int) MethodHeader.InitLocals) != 0;
74                                 if (methBody.LocalVarToken != 0)
75                                         VisitVariableDefinitionCollection (methBody.Variables);
76                                 ReadCilBody (methBody, br);
77                                 if ((fatflags & (int) MethodHeader.MoreSects) != 0)
78                                         ReadSection (methBody, br);
79                                 break;
80                         }
81                 }
82
83                 public static uint GetRid (int token)
84                 {
85                         return (uint) token & 0x00ffffff;
86                 }
87
88                 public static ParameterDefinition GetParameter (MethodBody body, int index)
89                 {
90                         if (body.Method.HasThis) {
91                                 if (index == 0)
92                                         return body.Method.This;
93                                 index--;
94                         }
95
96                         return body.Method.Parameters [index];
97                 }
98
99                 public static VariableDefinition GetVariable (MethodBody body, int index)
100                 {
101                         // bug 15727 - newer cecil does the same (in MethodDefinition.GetVariable)
102                         var variables = body.Variables;
103                         if (index < 0 || index >= variables.Count)
104                                 return null;
105                         return variables [index];
106                 }
107
108                 void ReadCilBody (MethodBody body, BinaryReader br)
109                 {
110                         long start = br.BaseStream.Position;
111                         Instruction last = null;
112                         m_instructions.Clear();
113                         InstructionCollection code = body.Instructions;
114                         GenericContext context = new GenericContext (body.Method);
115
116                         while (br.BaseStream.Position < start + body.CodeSize) {
117                                 OpCode op;
118                                 long offset = br.BaseStream.Position - start;
119                                 int cursor = br.ReadByte ();
120                                 if (cursor == 0xfe)
121                                         op = OpCodes.TwoBytesOpCode [br.ReadByte ()];
122                                 else
123                                         op = OpCodes.OneByteOpCode [cursor];
124
125                                 Instruction instr = new Instruction ((int) offset, op);
126                                 switch (op.OperandType) {
127                                 case OperandType.InlineNone :
128                                         break;
129                                 case OperandType.InlineSwitch :
130                                         uint length = br.ReadUInt32 ();
131                                         int [] branches = new int [length];
132                                         int [] buf = new int [length];
133                                         for (int i = 0; i < length; i++)
134                                                 buf [i] = br.ReadInt32 ();
135                                         for (int i = 0; i < length; i++)
136                                                 branches [i] = Convert.ToInt32 (br.BaseStream.Position - start + buf [i]);
137                                         instr.Operand = branches;
138                                         break;
139                                 case OperandType.ShortInlineBrTarget :
140                                         sbyte sbrtgt = br.ReadSByte ();
141                                         instr.Operand = Convert.ToInt32 (br.BaseStream.Position - start + sbrtgt);
142                                         break;
143                                 case OperandType.InlineBrTarget :
144                                         int brtgt = br.ReadInt32 ();
145                                         instr.Operand = Convert.ToInt32 (br.BaseStream.Position - start + brtgt);
146                                         break;
147                                 case OperandType.ShortInlineI :
148                                         if (op == OpCodes.Ldc_I4_S)
149                                                 instr.Operand = br.ReadSByte ();
150                                         else
151                                                 instr.Operand = br.ReadByte ();
152                                         break;
153                                 case OperandType.ShortInlineVar :
154                                         instr.Operand = GetVariable (body, br.ReadByte ());
155                                         break;
156                                 case OperandType.ShortInlineParam :
157                                         instr.Operand = GetParameter (body, br.ReadByte ());
158                                         break;
159                                 case OperandType.InlineSig :
160                                         instr.Operand = GetCallSiteAt (br.ReadInt32 (), context);
161                                         break;
162                                 case OperandType.InlineI :
163                                         instr.Operand = br.ReadInt32 ();
164                                         break;
165                                 case OperandType.InlineVar :
166                                         instr.Operand = GetVariable (body, br.ReadInt16 ());
167                                         break;
168                                 case OperandType.InlineParam :
169                                         instr.Operand = GetParameter (body, br.ReadInt16 ());
170                                         break;
171                                 case OperandType.InlineI8 :
172                                         instr.Operand = br.ReadInt64 ();
173                                         break;
174                                 case OperandType.ShortInlineR :
175                                         instr.Operand = br.ReadSingle ();
176                                         break;
177                                 case OperandType.InlineR :
178                                         instr.Operand = br.ReadDouble ();
179                                         break;
180                                 case OperandType.InlineString :
181                                         instr.Operand = m_root.Streams.UserStringsHeap [GetRid (br.ReadInt32 ())];
182                                         break;
183                                 case OperandType.InlineField :
184                                 case OperandType.InlineMethod :
185                                 case OperandType.InlineType :
186                                 case OperandType.InlineTok :
187                                         MetadataToken token = new MetadataToken (br.ReadInt32 ());
188                                         switch (token.TokenType) {
189                                         case TokenType.TypeDef:
190                                                 instr.Operand = m_reflectReader.GetTypeDefAt (token.RID);
191                                                 break;
192                                         case TokenType.TypeRef:
193                                                 instr.Operand = m_reflectReader.GetTypeRefAt (token.RID);
194                                                 break;
195                                         case TokenType.TypeSpec:
196                                                 instr.Operand = m_reflectReader.GetTypeSpecAt (token.RID, context);
197                                                 break;
198                                         case TokenType.Field:
199                                                 instr.Operand = m_reflectReader.GetFieldDefAt (token.RID);
200                                                 break;
201                                         case TokenType.Method:
202                                                 instr.Operand = m_reflectReader.GetMethodDefAt (token.RID);
203                                                 break;
204                                         case TokenType.MethodSpec:
205                                                 instr.Operand = m_reflectReader.GetMethodSpecAt (token.RID, context);
206                                                 break;
207                                         case TokenType.MemberRef:
208                                                 instr.Operand = m_reflectReader.GetMemberRefAt (token.RID, context);
209                                                 break;
210                                         default:
211                                                 throw new ReflectionException ("Wrong token: " + token);
212                                         }
213                                         break;
214                                 }
215
216                                 m_instructions.Add (instr.Offset, instr);
217
218                                 if (last != null) {
219                                         last.Next = instr;
220                                         instr.Previous = last;
221                                 }
222
223                                 last = instr;
224
225                                 code.Add (instr);
226                         }
227
228                         // resolve branches
229                         foreach (Instruction i in code) {
230                                 switch (i.OpCode.OperandType) {
231                                 case OperandType.ShortInlineBrTarget:
232                                 case OperandType.InlineBrTarget:
233                                         i.Operand = GetInstruction (body, (int) i.Operand);
234                                         break;
235                                 case OperandType.InlineSwitch:
236                                         int [] lbls = (int []) i.Operand;
237                                         Instruction [] instrs = new Instruction [lbls.Length];
238                                         for (int j = 0; j < lbls.Length; j++)
239                                                 instrs [j] = GetInstruction (body, lbls [j]);
240                                         i.Operand = instrs;
241                                         break;
242                                 }
243                         }
244
245                         if (m_reflectReader.SymbolReader != null)
246                                 m_reflectReader.SymbolReader.Read (body, m_instructions);
247                 }
248
249                 Instruction GetInstruction (MethodBody body, int offset)
250                 {
251                         Instruction instruction = m_instructions [offset] as Instruction;
252                         if (instruction != null)
253                                 return instruction;
254
255                         return body.Instructions.Outside;
256                 }
257
258                 void ReadSection (MethodBody body, BinaryReader br)
259                 {
260                         br.BaseStream.Position += 3;
261                         br.BaseStream.Position &= ~3;
262
263                         byte flags = br.ReadByte ();
264                         if ((flags & (byte) MethodDataSection.FatFormat) == 0) {
265                                 int length = br.ReadByte () / 12;
266                                 br.ReadBytes (2);
267
268                                 for (int i = 0; i < length; i++) {
269                                         ExceptionHandler eh = new ExceptionHandler (
270                                                 (ExceptionHandlerType) (br.ReadInt16 () & 0x7));
271                                         eh.TryStart = GetInstruction (body, Convert.ToInt32 (br.ReadInt16 ()));
272                                         eh.TryEnd = GetInstruction (body, eh.TryStart.Offset + Convert.ToInt32 (br.ReadByte ()));
273                                         eh.HandlerStart = GetInstruction (body, Convert.ToInt32 (br.ReadInt16 ()));
274                                         eh.HandlerEnd = GetInstruction (body, eh.HandlerStart.Offset + Convert.ToInt32 (br.ReadByte ()));
275                                         ReadExceptionHandlerEnd (eh, br, body);
276                                         body.ExceptionHandlers.Add (eh);
277                                 }
278                         } else {
279                                 br.BaseStream.Position--;
280                                 int length = (br.ReadInt32 () >> 8) / 24;
281                                 if ((flags & (int) MethodDataSection.EHTable) == 0)
282                                         br.ReadBytes (length * 24);
283                                 for (int i = 0; i < length; i++) {
284                                         ExceptionHandler eh = new ExceptionHandler (
285                                                 (ExceptionHandlerType) (br.ReadInt32 () & 0x7));
286                                         eh.TryStart = GetInstruction (body, br.ReadInt32 ());
287                                         eh.TryEnd = GetInstruction (body, eh.TryStart.Offset + br.ReadInt32 ());
288                                         eh.HandlerStart = GetInstruction (body, br.ReadInt32 ());
289                                         eh.HandlerEnd = GetInstruction (body, eh.HandlerStart.Offset + br.ReadInt32 ());
290                                         ReadExceptionHandlerEnd (eh, br, body);
291                                         body.ExceptionHandlers.Add (eh);
292                                 }
293                         }
294
295                         if ((flags & (byte) MethodDataSection.MoreSects) != 0)
296                                 ReadSection (body, br);
297                 }
298
299                 void ReadExceptionHandlerEnd (ExceptionHandler eh, BinaryReader br, MethodBody body)
300                 {
301                         switch (eh.Type) {
302                         case ExceptionHandlerType.Catch :
303                                 MetadataToken token = new MetadataToken (br.ReadInt32 ());
304                                 eh.CatchType = m_reflectReader.GetTypeDefOrRef (token, new GenericContext (body.Method));
305                                 break;
306                         case ExceptionHandlerType.Filter :
307                                 eh.FilterStart = GetInstruction (body, br.ReadInt32 ());
308                                 eh.FilterEnd = GetInstruction (body, eh.HandlerStart.Previous.Offset);
309                                 break;
310                         default :
311                                 br.ReadInt32 ();
312                                 break;
313                         }
314                 }
315
316                 CallSite GetCallSiteAt (int token, GenericContext context)
317                 {
318                         StandAloneSigTable sasTable = m_reflectReader.TableReader.GetStandAloneSigTable ();
319                         MethodSig ms = m_reflectReader.SigReader.GetStandAloneMethodSig (
320                                 sasTable [(int) GetRid (token) - 1].Signature);
321                         CallSite cs = new CallSite (ms.HasThis, ms.ExplicitThis,
322                                 ms.MethCallConv, m_reflectReader.GetMethodReturnType (ms, context));
323                         cs.MetadataToken = new MetadataToken (token);
324
325                         for (int i = 0; i < ms.ParamCount; i++) {
326                                 Param p = ms.Parameters [i];
327                                 cs.Parameters.Add (m_reflectReader.BuildParameterDefinition (i, p, context));
328                         }
329
330                         ReflectionReader.CreateSentinelIfNeeded (cs, ms);
331
332                         return cs;
333                 }
334
335                 public override void VisitVariableDefinitionCollection (VariableDefinitionCollection variables)
336                 {
337                         MethodBody body = variables.Container as MethodBody;
338                         if (body == null || body.LocalVarToken == 0)
339                                 return;
340
341                         StandAloneSigTable sasTable = m_reflectReader.TableReader.GetStandAloneSigTable ();
342                         StandAloneSigRow sasRow = sasTable [(int) GetRid (body.LocalVarToken) - 1];
343                         LocalVarSig sig = m_reflectReader.SigReader.GetLocalVarSig (sasRow.Signature);
344                         for (int i = 0; i < sig.Count; i++) {
345                                 LocalVarSig.LocalVariable lv = sig.LocalVariables [i];
346                                 TypeReference varType = m_reflectReader.GetTypeRefFromSig (
347                                         lv.Type, new GenericContext (body.Method));
348
349                                 if (lv.ByRef)
350                                         varType = new ReferenceType (varType);
351                                 if ((lv.Constraint & Constraint.Pinned) != 0)
352                                         varType = new PinnedType (varType);
353
354                                 varType = m_reflectReader.GetModifierType (lv.CustomMods, varType);
355
356                                 body.Variables.Add (new VariableDefinition (
357                                                 string.Concat ("V_", i), i, body.Method, varType));
358                         }
359                 }
360         }
361 }