2010-06-21 Atsushi Enomoto <atsushi@ximian.com>
[mcs.git] / class / Mono.Cecil / Mono.Cecil.Cil / CodeReader.cs
blob9fe965f5569a962ebdb8f631b43c4f36e5ce9bcd
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
29 namespace Mono.Cecil.Cil {
31 using System;
32 using System.Collections;
33 using System.IO;
35 using Mono.Cecil;
36 using Mono.Cecil.Metadata;
37 using Mono.Cecil.Signatures;
39 sealed class CodeReader : BaseCodeVisitor {
41 ReflectionReader m_reflectReader;
42 MetadataRoot m_root;
43 IDictionary m_instructions;
45 public CodeReader (ReflectionReader reflectReader)
47 m_reflectReader = reflectReader;
48 m_root = m_reflectReader.MetadataRoot;
49 m_instructions = new Hashtable ();
52 public override void VisitMethodBody (MethodBody body)
54 MethodDefinition meth = body.Method;
55 MethodBody methBody = body;
56 BinaryReader br = m_reflectReader.Module.ImageReader.MetadataReader.GetDataReader (meth.RVA);
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;
83 public static uint GetRid (int token)
85 return (uint) token & 0x00ffffff;
88 public static ParameterDefinition GetParameter (MethodBody body, int index)
90 if (body.Method.HasThis) {
91 if (index == 0)
92 return body.Method.This;
93 index--;
96 return body.Method.Parameters [index];
99 public static VariableDefinition GetVariable (MethodBody body, int index)
101 return body.Variables [index];
104 void ReadCilBody (MethodBody body, BinaryReader br)
106 long start = br.BaseStream.Position;
107 Instruction last = null;
108 m_instructions.Clear();
109 InstructionCollection code = body.Instructions;
110 GenericContext context = new GenericContext (body.Method);
112 while (br.BaseStream.Position < start + body.CodeSize) {
113 OpCode op;
114 long offset = br.BaseStream.Position - start;
115 int cursor = br.ReadByte ();
116 if (cursor == 0xfe)
117 op = OpCodes.TwoBytesOpCode [br.ReadByte ()];
118 else
119 op = OpCodes.OneByteOpCode [cursor];
121 Instruction instr = new Instruction ((int) offset, op);
122 switch (op.OperandType) {
123 case OperandType.InlineNone :
124 break;
125 case OperandType.InlineSwitch :
126 uint length = br.ReadUInt32 ();
127 int [] branches = new int [length];
128 int [] buf = new int [length];
129 for (int i = 0; i < length; i++)
130 buf [i] = br.ReadInt32 ();
131 for (int i = 0; i < length; i++)
132 branches [i] = Convert.ToInt32 (br.BaseStream.Position - start + buf [i]);
133 instr.Operand = branches;
134 break;
135 case OperandType.ShortInlineBrTarget :
136 sbyte sbrtgt = br.ReadSByte ();
137 instr.Operand = Convert.ToInt32 (br.BaseStream.Position - start + sbrtgt);
138 break;
139 case OperandType.InlineBrTarget :
140 int brtgt = br.ReadInt32 ();
141 instr.Operand = Convert.ToInt32 (br.BaseStream.Position - start + brtgt);
142 break;
143 case OperandType.ShortInlineI :
144 if (op == OpCodes.Ldc_I4_S)
145 instr.Operand = br.ReadSByte ();
146 else
147 instr.Operand = br.ReadByte ();
148 break;
149 case OperandType.ShortInlineVar :
150 instr.Operand = GetVariable (body, br.ReadByte ());
151 break;
152 case OperandType.ShortInlineParam :
153 instr.Operand = GetParameter (body, br.ReadByte ());
154 break;
155 case OperandType.InlineSig :
156 instr.Operand = GetCallSiteAt (br.ReadInt32 (), context);
157 break;
158 case OperandType.InlineI :
159 instr.Operand = br.ReadInt32 ();
160 break;
161 case OperandType.InlineVar :
162 instr.Operand = GetVariable (body, br.ReadInt16 ());
163 break;
164 case OperandType.InlineParam :
165 instr.Operand = GetParameter (body, br.ReadInt16 ());
166 break;
167 case OperandType.InlineI8 :
168 instr.Operand = br.ReadInt64 ();
169 break;
170 case OperandType.ShortInlineR :
171 instr.Operand = br.ReadSingle ();
172 break;
173 case OperandType.InlineR :
174 instr.Operand = br.ReadDouble ();
175 break;
176 case OperandType.InlineString :
177 instr.Operand = m_root.Streams.UserStringsHeap [GetRid (br.ReadInt32 ())];
178 break;
179 case OperandType.InlineField :
180 case OperandType.InlineMethod :
181 case OperandType.InlineType :
182 case OperandType.InlineTok :
183 MetadataToken token = new MetadataToken (br.ReadInt32 ());
184 switch (token.TokenType) {
185 case TokenType.TypeDef:
186 instr.Operand = m_reflectReader.GetTypeDefAt (token.RID);
187 break;
188 case TokenType.TypeRef:
189 instr.Operand = m_reflectReader.GetTypeRefAt (token.RID);
190 break;
191 case TokenType.TypeSpec:
192 instr.Operand = m_reflectReader.GetTypeSpecAt (token.RID, context);
193 break;
194 case TokenType.Field:
195 instr.Operand = m_reflectReader.GetFieldDefAt (token.RID);
196 break;
197 case TokenType.Method:
198 instr.Operand = m_reflectReader.GetMethodDefAt (token.RID);
199 break;
200 case TokenType.MethodSpec:
201 instr.Operand = m_reflectReader.GetMethodSpecAt (token.RID, context);
202 break;
203 case TokenType.MemberRef:
204 instr.Operand = m_reflectReader.GetMemberRefAt (token.RID, context);
205 break;
206 default:
207 throw new ReflectionException ("Wrong token: " + token);
209 break;
212 m_instructions.Add (instr.Offset, instr);
214 if (last != null) {
215 last.Next = instr;
216 instr.Previous = last;
219 last = instr;
221 code.Add (instr);
224 // resolve branches
225 foreach (Instruction i in code) {
226 switch (i.OpCode.OperandType) {
227 case OperandType.ShortInlineBrTarget:
228 case OperandType.InlineBrTarget:
229 i.Operand = GetInstruction (body, (int) i.Operand);
230 break;
231 case OperandType.InlineSwitch:
232 int [] lbls = (int []) i.Operand;
233 Instruction [] instrs = new Instruction [lbls.Length];
234 for (int j = 0; j < lbls.Length; j++)
235 instrs [j] = GetInstruction (body, lbls [j]);
236 i.Operand = instrs;
237 break;
241 if (m_reflectReader.SymbolReader != null)
242 m_reflectReader.SymbolReader.Read (body, m_instructions);
245 Instruction GetInstruction (MethodBody body, int offset)
247 Instruction instruction = m_instructions [offset] as Instruction;
248 if (instruction != null)
249 return instruction;
251 return body.Instructions.Outside;
254 void ReadSection (MethodBody body, BinaryReader br)
256 br.BaseStream.Position += 3;
257 br.BaseStream.Position &= ~3;
259 byte flags = br.ReadByte ();
260 if ((flags & (byte) MethodDataSection.FatFormat) == 0) {
261 int length = br.ReadByte () / 12;
262 br.ReadBytes (2);
264 for (int i = 0; i < length; i++) {
265 ExceptionHandler eh = new ExceptionHandler (
266 (ExceptionHandlerType) (br.ReadInt16 () & 0x7));
267 eh.TryStart = GetInstruction (body, Convert.ToInt32 (br.ReadInt16 ()));
268 eh.TryEnd = GetInstruction (body, eh.TryStart.Offset + Convert.ToInt32 (br.ReadByte ()));
269 eh.HandlerStart = GetInstruction (body, Convert.ToInt32 (br.ReadInt16 ()));
270 eh.HandlerEnd = GetInstruction (body, eh.HandlerStart.Offset + Convert.ToInt32 (br.ReadByte ()));
271 ReadExceptionHandlerEnd (eh, br, body);
272 body.ExceptionHandlers.Add (eh);
274 } else {
275 br.BaseStream.Position--;
276 int length = (br.ReadInt32 () >> 8) / 24;
277 if ((flags & (int) MethodDataSection.EHTable) == 0)
278 br.ReadBytes (length * 24);
279 for (int i = 0; i < length; i++) {
280 ExceptionHandler eh = new ExceptionHandler (
281 (ExceptionHandlerType) (br.ReadInt32 () & 0x7));
282 eh.TryStart = GetInstruction (body, br.ReadInt32 ());
283 eh.TryEnd = GetInstruction (body, eh.TryStart.Offset + br.ReadInt32 ());
284 eh.HandlerStart = GetInstruction (body, br.ReadInt32 ());
285 eh.HandlerEnd = GetInstruction (body, eh.HandlerStart.Offset + br.ReadInt32 ());
286 ReadExceptionHandlerEnd (eh, br, body);
287 body.ExceptionHandlers.Add (eh);
291 if ((flags & (byte) MethodDataSection.MoreSects) != 0)
292 ReadSection (body, br);
295 void ReadExceptionHandlerEnd (ExceptionHandler eh, BinaryReader br, MethodBody body)
297 switch (eh.Type) {
298 case ExceptionHandlerType.Catch :
299 MetadataToken token = new MetadataToken (br.ReadInt32 ());
300 eh.CatchType = m_reflectReader.GetTypeDefOrRef (token, new GenericContext (body.Method));
301 break;
302 case ExceptionHandlerType.Filter :
303 eh.FilterStart = GetInstruction (body, br.ReadInt32 ());
304 eh.FilterEnd = GetInstruction (body, eh.HandlerStart.Previous.Offset);
305 break;
306 default :
307 br.ReadInt32 ();
308 break;
312 CallSite GetCallSiteAt (int token, GenericContext context)
314 StandAloneSigTable sasTable = m_reflectReader.TableReader.GetStandAloneSigTable ();
315 MethodSig ms = m_reflectReader.SigReader.GetStandAloneMethodSig (
316 sasTable [(int) GetRid (token) - 1].Signature);
317 CallSite cs = new CallSite (ms.HasThis, ms.ExplicitThis,
318 ms.MethCallConv, m_reflectReader.GetMethodReturnType (ms, context));
319 cs.MetadataToken = new MetadataToken (token);
321 for (int i = 0; i < ms.ParamCount; i++) {
322 Param p = ms.Parameters [i];
323 cs.Parameters.Add (m_reflectReader.BuildParameterDefinition (i, p, context));
326 ReflectionReader.CreateSentinelIfNeeded (cs, ms);
328 return cs;
331 public override void VisitVariableDefinitionCollection (VariableDefinitionCollection variables)
333 MethodBody body = variables.Container as MethodBody;
334 if (body == null || body.LocalVarToken == 0)
335 return;
337 StandAloneSigTable sasTable = m_reflectReader.TableReader.GetStandAloneSigTable ();
338 StandAloneSigRow sasRow = sasTable [(int) GetRid (body.LocalVarToken) - 1];
339 LocalVarSig sig = m_reflectReader.SigReader.GetLocalVarSig (sasRow.Signature);
340 for (int i = 0; i < sig.Count; i++) {
341 LocalVarSig.LocalVariable lv = sig.LocalVariables [i];
342 TypeReference varType = m_reflectReader.GetTypeRefFromSig (
343 lv.Type, new GenericContext (body.Method));
345 if (lv.ByRef)
346 varType = new ReferenceType (varType);
347 if ((lv.Constraint & Constraint.Pinned) != 0)
348 varType = new PinnedType (varType);
350 varType = m_reflectReader.GetModifierType (lv.CustomMods, varType);
352 body.Variables.Add (new VariableDefinition (
353 string.Concat ("V_", i), i, body.Method, varType));