The stripper does not care if some variables can't be referenced (they'll be removed...
[mono-project.git] / mcs / tools / cil-strip / Mono.Cecil.Cil / CodeReader.cs
blob4e7ca90fa6ecc9d56c39c9fa43c35ef0006d4a38
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 // 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];
108 void ReadCilBody (MethodBody body, BinaryReader br)
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);
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];
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);
213 break;
216 m_instructions.Add (instr.Offset, instr);
218 if (last != null) {
219 last.Next = instr;
220 instr.Previous = last;
223 last = instr;
225 code.Add (instr);
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;
245 if (m_reflectReader.SymbolReader != null)
246 m_reflectReader.SymbolReader.Read (body, m_instructions);
249 Instruction GetInstruction (MethodBody body, int offset)
251 Instruction instruction = m_instructions [offset] as Instruction;
252 if (instruction != null)
253 return instruction;
255 return body.Instructions.Outside;
258 void ReadSection (MethodBody body, BinaryReader br)
260 br.BaseStream.Position += 3;
261 br.BaseStream.Position &= ~3;
263 byte flags = br.ReadByte ();
264 if ((flags & (byte) MethodDataSection.FatFormat) == 0) {
265 int length = br.ReadByte () / 12;
266 br.ReadBytes (2);
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);
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);
295 if ((flags & (byte) MethodDataSection.MoreSects) != 0)
296 ReadSection (body, br);
299 void ReadExceptionHandlerEnd (ExceptionHandler eh, BinaryReader br, MethodBody body)
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;
316 CallSite GetCallSiteAt (int token, GenericContext context)
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);
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));
330 ReflectionReader.CreateSentinelIfNeeded (cs, ms);
332 return cs;
335 public override void VisitVariableDefinitionCollection (VariableDefinitionCollection variables)
337 MethodBody body = variables.Container as MethodBody;
338 if (body == null || body.LocalVarToken == 0)
339 return;
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));
349 if (lv.ByRef)
350 varType = new ReferenceType (varType);
351 if ((lv.Constraint & Constraint.Pinned) != 0)
352 varType = new PinnedType (varType);
354 varType = m_reflectReader.GetModifierType (lv.CustomMods, varType);
356 body.Variables.Add (new VariableDefinition (
357 string.Concat ("V_", i), i, body.Method, varType));