5 // Jb Evain (jbevain@gmail.com)
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 {
33 internal sealed class MethodBody : IVariableDefinitionProvider, IScopeProvider, ICodeVisitable {
35 MethodDefinition m_method;
41 InstructionCollection m_instructions;
42 ExceptionHandlerCollection m_exceptions;
43 VariableDefinitionCollection m_variables;
44 ScopeCollection m_scopes;
46 private CilWorker m_cilWorker;
48 public MethodDefinition Method {
49 get { return m_method; }
53 get { return m_maxStack; }
54 set { m_maxStack = value; }
58 get { return m_codeSize; }
59 set { m_codeSize = value; }
62 public bool InitLocals {
63 get { return m_initLocals; }
64 set { m_initLocals = value; }
67 public int LocalVarToken {
68 get { return m_localVarToken; }
69 set { m_localVarToken = value; }
72 public CilWorker CilWorker {
74 if (m_cilWorker == null)
75 m_cilWorker = new CilWorker (this);
78 set { m_cilWorker = value; }
81 public InstructionCollection Instructions {
82 get { return m_instructions; }
85 public bool HasExceptionHandlers {
86 get { return m_exceptions != null && m_exceptions.Count > 0; }
89 public ExceptionHandlerCollection ExceptionHandlers {
91 if (m_exceptions == null)
92 m_exceptions = new ExceptionHandlerCollection (this);
97 public bool HasVariables {
98 get { return m_variables != null && m_variables.Count > 0; }
101 public VariableDefinitionCollection Variables {
103 if (m_variables == null)
104 m_variables = new VariableDefinitionCollection (this);
109 public bool HasScopes {
110 get { return m_scopes != null && m_scopes.Count > 0; }
113 public ScopeCollection Scopes {
115 if (m_scopes == null)
116 m_scopes = new ScopeCollection (this);
121 public MethodBody (MethodDefinition meth)
124 // there is always a RET instruction (if a body is present)
125 m_instructions = new InstructionCollection (this);
128 internal static Instruction GetInstruction (MethodBody oldBody, MethodBody newBody, Instruction i)
130 int pos = oldBody.Instructions.IndexOf (i);
131 if (pos > -1 && pos < newBody.Instructions.Count)
132 return newBody.Instructions [pos];
134 return newBody.Instructions.Outside;
137 internal static MethodBody Clone (MethodBody body, MethodDefinition parent, ImportContext context)
139 MethodBody nb = new MethodBody (parent);
140 nb.MaxStack = body.MaxStack;
141 nb.InitLocals = body.InitLocals;
142 nb.CodeSize = body.CodeSize;
144 CilWorker worker = nb.CilWorker;
146 if (body.HasVariables) {
147 foreach (VariableDefinition var in body.Variables)
148 nb.Variables.Add (new VariableDefinition (
149 var.Name, var.Index, parent,
150 context.Import (var.VariableType)));
153 foreach (Instruction instr in body.Instructions) {
154 Instruction ni = new Instruction (instr.OpCode);
156 switch (instr.OpCode.OperandType) {
157 case OperandType.InlineParam :
158 case OperandType.ShortInlineParam :
159 if (instr.Operand == body.Method.This)
160 ni.Operand = nb.Method.This;
162 int param = body.Method.Parameters.IndexOf ((ParameterDefinition) instr.Operand);
163 ni.Operand = parent.Parameters [param];
166 case OperandType.InlineVar :
167 case OperandType.ShortInlineVar :
168 int var = body.Variables.IndexOf ((VariableDefinition) instr.Operand);
169 ni.Operand = nb.Variables [var];
171 case OperandType.InlineField :
172 ni.Operand = context.Import ((FieldReference) instr.Operand);
174 case OperandType.InlineMethod :
175 ni.Operand = context.Import ((MethodReference) instr.Operand);
177 case OperandType.InlineType :
178 ni.Operand = context.Import ((TypeReference) instr.Operand);
180 case OperandType.InlineTok :
181 if (instr.Operand is TypeReference)
182 ni.Operand = context.Import ((TypeReference) instr.Operand);
183 else if (instr.Operand is FieldReference)
184 ni.Operand = context.Import ((FieldReference) instr.Operand);
185 else if (instr.Operand is MethodReference)
186 ni.Operand = context.Import ((MethodReference) instr.Operand);
188 case OperandType.ShortInlineBrTarget :
189 case OperandType.InlineBrTarget :
190 case OperandType.InlineSwitch :
193 ni.Operand = instr.Operand;
200 for (int i = 0; i < body.Instructions.Count; i++) {
201 Instruction instr = nb.Instructions [i];
202 Instruction oldi = body.Instructions [i];
204 if (instr.OpCode.OperandType == OperandType.InlineSwitch) {
205 Instruction [] olds = (Instruction []) oldi.Operand;
206 Instruction [] targets = new Instruction [olds.Length];
208 for (int j = 0; j < targets.Length; j++)
209 targets [j] = GetInstruction (body, nb, olds [j]);
211 instr.Operand = targets;
212 } else if (instr.OpCode.OperandType == OperandType.ShortInlineBrTarget || instr.OpCode.OperandType == OperandType.InlineBrTarget)
213 instr.Operand = GetInstruction (body, nb, (Instruction) oldi.Operand);
216 if (!body.HasExceptionHandlers)
219 foreach (ExceptionHandler eh in body.ExceptionHandlers) {
220 ExceptionHandler neh = new ExceptionHandler (eh.Type);
221 neh.TryStart = GetInstruction (body, nb, eh.TryStart);
222 neh.TryEnd = GetInstruction (body, nb, eh.TryEnd);
223 neh.HandlerStart = GetInstruction (body, nb, eh.HandlerStart);
224 neh.HandlerEnd = GetInstruction (body, nb, eh.HandlerEnd);
227 case ExceptionHandlerType.Catch :
228 neh.CatchType = context.Import (eh.CatchType);
230 case ExceptionHandlerType.Filter :
231 neh.FilterStart = GetInstruction (body, nb, eh.FilterStart);
232 neh.FilterEnd = GetInstruction (body, nb, eh.FilterEnd);
236 nb.ExceptionHandlers.Add (neh);
242 public void Simplify ()
244 foreach (Instruction i in this.Instructions) {
245 if (i.OpCode.OpCodeType != OpCodeType.Macro)
248 switch (i.OpCode.Code) {
250 Modify (i, OpCodes.Ldarg,
251 CodeReader.GetParameter (this, 0));
254 Modify (i, OpCodes.Ldarg,
255 CodeReader.GetParameter (this, 1));
258 Modify (i, OpCodes.Ldarg,
259 CodeReader.GetParameter (this, 2));
262 Modify (i, OpCodes.Ldarg,
263 CodeReader.GetParameter (this, 3));
266 Modify (i, OpCodes.Ldloc,
267 CodeReader.GetVariable (this, 0));
270 Modify (i, OpCodes.Ldloc,
271 CodeReader.GetVariable (this, 1));
274 Modify (i, OpCodes.Ldloc,
275 CodeReader.GetVariable (this, 2));
278 Modify (i, OpCodes.Ldloc,
279 CodeReader.GetVariable (this, 3));
282 Modify (i, OpCodes.Stloc,
283 CodeReader.GetVariable (this, 0));
286 Modify (i, OpCodes.Stloc,
287 CodeReader.GetVariable (this, 1));
290 Modify (i, OpCodes.Stloc,
291 CodeReader.GetVariable (this, 2));
294 Modify (i, OpCodes.Stloc,
295 CodeReader.GetVariable (this, 3));
298 i.OpCode = OpCodes.Ldarg;
301 i.OpCode = OpCodes.Ldarga;
304 i.OpCode = OpCodes.Starg;
307 i.OpCode = OpCodes.Ldloc;
310 i.OpCode = OpCodes.Ldloca;
313 i.OpCode = OpCodes.Stloc;
315 case Code.Ldc_I4_M1 :
316 Modify (i, OpCodes.Ldc_I4, -1);
319 Modify (i, OpCodes.Ldc_I4, 0);
322 Modify (i, OpCodes.Ldc_I4, 1);
325 Modify (i, OpCodes.Ldc_I4, 2);
328 Modify (i, OpCodes.Ldc_I4, 3);
331 Modify (i, OpCodes.Ldc_I4, 4);
334 Modify (i, OpCodes.Ldc_I4, 5);
337 Modify (i, OpCodes.Ldc_I4, 6);
340 Modify (i, OpCodes.Ldc_I4, 7);
343 Modify (i, OpCodes.Ldc_I4, 8);
346 i.OpCode = OpCodes.Ldc_I4;
347 i.Operand = (int) (sbyte) i.Operand;
350 i.OpCode = OpCodes.Br;
352 case Code.Brfalse_S :
353 i.OpCode = OpCodes.Brfalse;
356 i.OpCode = OpCodes.Brtrue;
359 i.OpCode = OpCodes.Beq;
362 i.OpCode = OpCodes.Bge;
365 i.OpCode = OpCodes.Bgt;
368 i.OpCode = OpCodes.Ble;
371 i.OpCode = OpCodes.Blt;
374 i.OpCode = OpCodes.Bne_Un;
377 i.OpCode = OpCodes.Bge_Un;
380 i.OpCode = OpCodes.Bgt_Un;
383 i.OpCode = OpCodes.Ble_Un;
386 i.OpCode = OpCodes.Blt_Un;
389 i.OpCode = OpCodes.Leave;
395 public void Optimize ()
397 foreach (Instruction instr in m_instructions) {
399 switch (instr.OpCode.Code) {
401 index = m_method.Parameters.IndexOf ((ParameterDefinition) instr.Operand);
402 if (index == -1 && instr.Operand == m_method.This)
404 else if (m_method.HasThis)
409 Modify (instr, OpCodes.Ldarg_0, null);
412 Modify (instr, OpCodes.Ldarg_1, null);
415 Modify (instr, OpCodes.Ldarg_2, null);
418 Modify (instr, OpCodes.Ldarg_3, null);
422 Modify (instr, OpCodes.Ldarg_S, instr.Operand);
427 index = m_variables.IndexOf ((VariableDefinition) instr.Operand);
430 Modify (instr, OpCodes.Ldloc_0, null);
433 Modify (instr, OpCodes.Ldloc_1, null);
436 Modify (instr, OpCodes.Ldloc_2, null);
439 Modify (instr, OpCodes.Ldloc_3, null);
443 Modify (instr, OpCodes.Ldloc_S, instr.Operand);
448 index = m_variables.IndexOf ((VariableDefinition) instr.Operand);
451 Modify (instr, OpCodes.Stloc_0, null);
454 Modify (instr, OpCodes.Stloc_1, null);
457 Modify (instr, OpCodes.Stloc_2, null);
460 Modify (instr, OpCodes.Stloc_3, null);
464 Modify (instr, OpCodes.Stloc_S, instr.Operand);
469 index = m_method.Parameters.IndexOf ((ParameterDefinition) instr.Operand);
470 if (index == -1 && instr.Operand == m_method.This)
472 else if (m_method.HasThis)
475 Modify (instr, OpCodes.Ldarga_S, instr.Operand);
478 if (m_variables.IndexOf ((VariableDefinition) instr.Operand) < 256)
479 Modify (instr, OpCodes.Ldloca_S, instr.Operand);
482 int i = (int) instr.Operand;
485 Modify (instr, OpCodes.Ldc_I4_M1, null);
488 Modify (instr, OpCodes.Ldc_I4_0, null);
491 Modify (instr, OpCodes.Ldc_I4_1, null);
494 Modify (instr, OpCodes.Ldc_I4_2, null);
497 Modify (instr, OpCodes.Ldc_I4_3, null);
500 Modify (instr, OpCodes.Ldc_I4_4, null);
503 Modify (instr, OpCodes.Ldc_I4_5, null);
506 Modify (instr, OpCodes.Ldc_I4_6, null);
509 Modify (instr, OpCodes.Ldc_I4_7, null);
512 Modify (instr, OpCodes.Ldc_I4_8, null);
515 if (i >= -128 && i < 128)
516 Modify (instr, OpCodes.Ldc_I4_S, (sbyte) i);
526 void OptimizeBranches ()
530 foreach (Instruction instr in m_instructions) {
531 if (instr.OpCode.OperandType != OperandType.InlineBrTarget)
534 if (OptimizeBranch (instr))
539 static bool OptimizeBranch (Instruction instr)
541 int offset = ((Instruction) instr.Operand).Offset - (instr.Offset + instr.OpCode.Size + 4);
542 if (! (offset >= -128 && offset <= 127))
545 switch (instr.OpCode.Code) {
547 instr.OpCode = OpCodes.Br_S;
550 instr.OpCode = OpCodes.Brfalse_S;
553 instr.OpCode = OpCodes.Brtrue_S;
556 instr.OpCode = OpCodes.Beq_S;
559 instr.OpCode = OpCodes.Bge_S;
562 instr.OpCode = OpCodes.Bgt_S;
565 instr.OpCode = OpCodes.Ble_S;
568 instr.OpCode = OpCodes.Blt_S;
571 instr.OpCode = OpCodes.Bne_Un_S;
574 instr.OpCode = OpCodes.Bge_Un_S;
577 instr.OpCode = OpCodes.Bgt_Un_S;
580 instr.OpCode = OpCodes.Ble_Un_S;
583 instr.OpCode = OpCodes.Blt_Un_S;
586 instr.OpCode = OpCodes.Leave_S;
593 void ComputeOffsets ()
597 foreach (Instruction instr in m_instructions) {
598 instr.Offset = offset;
599 offset += instr.GetSize ();
603 static void Modify (Instruction i, OpCode op, object operand)
609 public void Accept (ICodeVisitor visitor)
611 visitor.VisitMethodBody (this);
613 m_variables.Accept (visitor);
614 m_instructions.Accept (visitor);
615 if (HasExceptionHandlers)
616 m_exceptions.Accept (visitor);
618 m_scopes.Accept (visitor);
620 visitor.TerminateMethodBody (this);