56e08b371aa7ceba91dffb949c69777ba9fa187b
[mono.git] / mcs / tools / cil-strip / Mono.Cecil.Cil / MethodBody.cs
1 //
2 // MethodBody.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@gmail.com)
6 //
7 // (C) 2005 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 Mono.Cecil;
32
33         internal sealed class MethodBody : IVariableDefinitionProvider, IScopeProvider, ICodeVisitable {
34
35                 MethodDefinition m_method;
36                 int m_maxStack;
37                 int m_codeSize;
38                 bool m_initLocals;
39                 int m_localVarToken;
40
41                 InstructionCollection m_instructions;
42                 ExceptionHandlerCollection m_exceptions;
43                 VariableDefinitionCollection m_variables;
44                 ScopeCollection m_scopes;
45
46                 private CilWorker m_cilWorker;
47
48                 public MethodDefinition Method {
49                         get { return m_method; }
50                 }
51
52                 public int MaxStack {
53                         get { return m_maxStack; }
54                         set { m_maxStack = value; }
55                 }
56
57                 public int CodeSize {
58                         get { return m_codeSize; }
59                         set { m_codeSize = value; }
60                 }
61
62                 public bool InitLocals {
63                         get { return m_initLocals; }
64                         set { m_initLocals = value; }
65                 }
66
67                 public int LocalVarToken {
68                         get { return m_localVarToken; }
69                         set { m_localVarToken = value; }
70                 }
71
72                 public CilWorker CilWorker {
73                         get {
74                                 if (m_cilWorker == null)
75                                         m_cilWorker = new CilWorker (this);
76                                 return m_cilWorker;
77                         }
78                         set { m_cilWorker = value; }
79                 }
80
81                 public InstructionCollection Instructions {
82                         get { return m_instructions; }
83                 }
84
85                 public bool HasExceptionHandlers {
86                         get { return m_exceptions != null && m_exceptions.Count > 0; }
87                 }
88
89                 public ExceptionHandlerCollection ExceptionHandlers {
90                         get {
91                                 if (m_exceptions == null)
92                                         m_exceptions = new ExceptionHandlerCollection (this);
93                                 return m_exceptions;
94                         }
95                 }
96
97                 public bool HasVariables {
98                         get { return m_variables != null && m_variables.Count > 0; }
99                 }
100
101                 public VariableDefinitionCollection Variables {
102                         get {
103                                 if (m_variables == null)
104                                         m_variables = new VariableDefinitionCollection (this);
105                                 return m_variables;
106                         }
107                 }
108
109                 public bool HasScopes {
110                         get { return m_scopes != null && m_scopes.Count > 0; }
111                 }
112
113                 public ScopeCollection Scopes {
114                         get {
115                                 if (m_scopes == null)
116                                         m_scopes = new ScopeCollection (this);
117                                 return m_scopes;
118                         }
119                 }
120
121                 public MethodBody (MethodDefinition meth)
122                 {
123                         m_method = meth;
124                         // there is always a RET instruction (if a body is present)
125                         m_instructions = new InstructionCollection (this);
126                 }
127
128                 internal static Instruction GetInstruction (MethodBody oldBody, MethodBody newBody, Instruction i)
129                 {
130                         int pos = oldBody.Instructions.IndexOf (i);
131                         if (pos > -1 && pos < newBody.Instructions.Count)
132                                 return newBody.Instructions [pos];
133
134                         return newBody.Instructions.Outside;
135                 }
136
137                 internal static MethodBody Clone (MethodBody body, MethodDefinition parent, ImportContext context)
138                 {
139                         MethodBody nb = new MethodBody (parent);
140                         nb.MaxStack = body.MaxStack;
141                         nb.InitLocals = body.InitLocals;
142                         nb.CodeSize = body.CodeSize;
143
144                         CilWorker worker = nb.CilWorker;
145
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)));
151                         }
152
153                         foreach (Instruction instr in body.Instructions) {
154                                 Instruction ni = new Instruction (instr.OpCode);
155
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;
161                                         else {
162                                                 int param = body.Method.Parameters.IndexOf ((ParameterDefinition) instr.Operand);
163                                                 ni.Operand = parent.Parameters [param];
164                                         }
165                                         break;
166                                 case OperandType.InlineVar :
167                                 case OperandType.ShortInlineVar :
168                                         int var = body.Variables.IndexOf ((VariableDefinition) instr.Operand);
169                                         ni.Operand = nb.Variables [var];
170                                         break;
171                                 case OperandType.InlineField :
172                                         ni.Operand = context.Import ((FieldReference) instr.Operand);
173                                         break;
174                                 case OperandType.InlineMethod :
175                                         ni.Operand = context.Import ((MethodReference) instr.Operand);
176                                         break;
177                                 case OperandType.InlineType :
178                                         ni.Operand = context.Import ((TypeReference) instr.Operand);
179                                         break;
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);
187                                         break;
188                                 case OperandType.ShortInlineBrTarget :
189                                 case OperandType.InlineBrTarget :
190                                 case OperandType.InlineSwitch :
191                                         break;
192                                 default :
193                                         ni.Operand = instr.Operand;
194                                         break;
195                                 }
196
197                                 worker.Append (ni);
198                         }
199
200                         for (int i = 0; i < body.Instructions.Count; i++) {
201                                 Instruction instr = nb.Instructions [i];
202                                 Instruction oldi = body.Instructions [i];
203
204                                 if (instr.OpCode.OperandType == OperandType.InlineSwitch) {
205                                         Instruction [] olds = (Instruction []) oldi.Operand;
206                                         Instruction [] targets = new Instruction [olds.Length];
207
208                                         for (int j = 0; j < targets.Length; j++)
209                                                 targets [j] = GetInstruction (body, nb, olds [j]);
210
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);
214                         }
215
216                         if (!body.HasExceptionHandlers)
217                                 return nb;
218
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);
225
226                                 switch (eh.Type) {
227                                 case ExceptionHandlerType.Catch :
228                                         neh.CatchType = context.Import (eh.CatchType);
229                                         break;
230                                 case ExceptionHandlerType.Filter :
231                                         neh.FilterStart = GetInstruction (body, nb, eh.FilterStart);
232                                         neh.FilterEnd = GetInstruction (body, nb, eh.FilterEnd);
233                                         break;
234                                 }
235
236                                 nb.ExceptionHandlers.Add (neh);
237                         }
238
239                         return nb;
240                 }
241
242                 public void Simplify ()
243                 {
244                         foreach (Instruction i in this.Instructions) {
245                                 if (i.OpCode.OpCodeType != OpCodeType.Macro)
246                                         continue;
247
248                                 switch (i.OpCode.Code) {
249                                 case Code.Ldarg_0 :
250                                         Modify (i, OpCodes.Ldarg,
251                                                 CodeReader.GetParameter (this, 0));
252                                         break;
253                                 case Code.Ldarg_1 :
254                                         Modify (i, OpCodes.Ldarg,
255                                                 CodeReader.GetParameter (this, 1));
256                                         break;
257                                 case Code.Ldarg_2 :
258                                         Modify (i, OpCodes.Ldarg,
259                                                 CodeReader.GetParameter (this, 2));
260                                         break;
261                                 case Code.Ldarg_3 :
262                                         Modify (i, OpCodes.Ldarg,
263                                                 CodeReader.GetParameter (this, 3));
264                                         break;
265                                 case Code.Ldloc_0 :
266                                         Modify (i, OpCodes.Ldloc,
267                                                 CodeReader.GetVariable (this, 0));
268                                         break;
269                                 case Code.Ldloc_1 :
270                                         Modify (i, OpCodes.Ldloc,
271                                                 CodeReader.GetVariable (this, 1));
272                                         break;
273                                 case Code.Ldloc_2 :
274                                         Modify (i, OpCodes.Ldloc,
275                                                 CodeReader.GetVariable (this, 2));
276                                         break;
277                                 case Code.Ldloc_3 :
278                                         Modify (i, OpCodes.Ldloc,
279                                                 CodeReader.GetVariable (this, 3));
280                                         break;
281                                 case Code.Stloc_0 :
282                                         Modify (i, OpCodes.Stloc,
283                                                 CodeReader.GetVariable (this, 0));
284                                         break;
285                                 case Code.Stloc_1 :
286                                         Modify (i, OpCodes.Stloc,
287                                                 CodeReader.GetVariable (this, 1));
288                                         break;
289                                 case Code.Stloc_2 :
290                                         Modify (i, OpCodes.Stloc,
291                                                 CodeReader.GetVariable (this, 2));
292                                         break;
293                                 case Code.Stloc_3 :
294                                         Modify (i, OpCodes.Stloc,
295                                                 CodeReader.GetVariable (this, 3));
296                                         break;
297                                 case Code.Ldarg_S :
298                                         i.OpCode = OpCodes.Ldarg;
299                                         break;
300                                 case Code.Ldarga_S :
301                                         i.OpCode = OpCodes.Ldarga;
302                                         break;
303                                 case Code.Starg_S :
304                                         i.OpCode = OpCodes.Starg;
305                                         break;
306                                 case Code.Ldloc_S :
307                                         i.OpCode = OpCodes.Ldloc;
308                                         break;
309                                 case Code.Ldloca_S :
310                                         i.OpCode = OpCodes.Ldloca;
311                                         break;
312                                 case Code.Stloc_S :
313                                         i.OpCode = OpCodes.Stloc;
314                                         break;
315                                 case Code.Ldc_I4_M1 :
316                                         Modify (i, OpCodes.Ldc_I4, -1);
317                                         break;
318                                 case Code.Ldc_I4_0 :
319                                         Modify (i, OpCodes.Ldc_I4, 0);
320                                         break;
321                                 case Code.Ldc_I4_1 :
322                                         Modify (i, OpCodes.Ldc_I4, 1);
323                                         break;
324                                 case Code.Ldc_I4_2 :
325                                         Modify (i, OpCodes.Ldc_I4, 2);
326                                         break;
327                                 case Code.Ldc_I4_3 :
328                                         Modify (i, OpCodes.Ldc_I4, 3);
329                                         break;
330                                 case Code.Ldc_I4_4 :
331                                         Modify (i, OpCodes.Ldc_I4, 4);
332                                         break;
333                                 case Code.Ldc_I4_5 :
334                                         Modify (i, OpCodes.Ldc_I4, 5);
335                                         break;
336                                 case Code.Ldc_I4_6 :
337                                         Modify (i, OpCodes.Ldc_I4, 6);
338                                         break;
339                                 case Code.Ldc_I4_7 :
340                                         Modify (i, OpCodes.Ldc_I4, 7);
341                                         break;
342                                 case Code.Ldc_I4_8 :
343                                         Modify (i, OpCodes.Ldc_I4, 8);
344                                         break;
345                                 case Code.Ldc_I4_S :
346                                         i.OpCode = OpCodes.Ldc_I4;
347                                         i.Operand = (int) (sbyte) i.Operand;
348                                         break;
349                                 case Code.Br_S :
350                                         i.OpCode = OpCodes.Br;
351                                         break;
352                                 case Code.Brfalse_S :
353                                         i.OpCode = OpCodes.Brfalse;
354                                         break;
355                                 case Code.Brtrue_S :
356                                         i.OpCode = OpCodes.Brtrue;
357                                         break;
358                                 case Code.Beq_S :
359                                         i.OpCode = OpCodes.Beq;
360                                         break;
361                                 case Code.Bge_S :
362                                         i.OpCode = OpCodes.Bge;
363                                         break;
364                                 case Code.Bgt_S :
365                                         i.OpCode = OpCodes.Bgt;
366                                         break;
367                                 case Code.Ble_S :
368                                         i.OpCode = OpCodes.Ble;
369                                         break;
370                                 case Code.Blt_S :
371                                         i.OpCode = OpCodes.Blt;
372                                         break;
373                                 case Code.Bne_Un_S :
374                                         i.OpCode = OpCodes.Bne_Un;
375                                         break;
376                                 case Code.Bge_Un_S :
377                                         i.OpCode = OpCodes.Bge_Un;
378                                         break;
379                                 case Code.Bgt_Un_S :
380                                         i.OpCode = OpCodes.Bgt_Un;
381                                         break;
382                                 case Code.Ble_Un_S :
383                                         i.OpCode = OpCodes.Ble_Un;
384                                         break;
385                                 case Code.Blt_Un_S :
386                                         i.OpCode = OpCodes.Blt_Un;
387                                         break;
388                                 case Code.Leave_S :
389                                         i.OpCode = OpCodes.Leave;
390                                         break;
391                                 }
392                         }
393                 }
394
395                 public void Optimize ()
396                 {
397                         foreach (Instruction instr in m_instructions) {
398                                 int index;
399                                 switch (instr.OpCode.Code) {
400                                 case Code.Ldarg:
401                                         index = m_method.Parameters.IndexOf ((ParameterDefinition) instr.Operand);
402                                         if (index == -1 && instr.Operand == m_method.This)
403                                                 index = 0;
404                                         else if (m_method.HasThis)
405                                                 index++;
406
407                                         switch (index) {
408                                         case 0:
409                                                 Modify (instr, OpCodes.Ldarg_0, null);
410                                                 break;
411                                         case 1:
412                                                 Modify (instr, OpCodes.Ldarg_1, null);
413                                                 break;
414                                         case 2:
415                                                 Modify (instr, OpCodes.Ldarg_2, null);
416                                                 break;
417                                         case 3:
418                                                 Modify (instr, OpCodes.Ldarg_3, null);
419                                                 break;
420                                         default:
421                                                 if (index < 256)
422                                                         Modify (instr, OpCodes.Ldarg_S, instr.Operand);
423                                                 break;
424                                         }
425                                         break;
426                                 case Code.Ldloc:
427                                         index = m_variables.IndexOf ((VariableDefinition) instr.Operand);
428                                         switch (index) {
429                                         case 0:
430                                                 Modify (instr, OpCodes.Ldloc_0, null);
431                                                 break;
432                                         case 1:
433                                                 Modify (instr, OpCodes.Ldloc_1, null);
434                                                 break;
435                                         case 2:
436                                                 Modify (instr, OpCodes.Ldloc_2, null);
437                                                 break;
438                                         case 3:
439                                                 Modify (instr, OpCodes.Ldloc_3, null);
440                                                 break;
441                                         default:
442                                                 if (index < 256)
443                                                         Modify (instr, OpCodes.Ldloc_S, instr.Operand);
444                                                 break;
445                                         }
446                                         break;
447                                 case Code.Stloc:
448                                         index = m_variables.IndexOf ((VariableDefinition) instr.Operand);
449                                         switch (index) {
450                                         case 0:
451                                                 Modify (instr, OpCodes.Stloc_0, null);
452                                                 break;
453                                         case 1:
454                                                 Modify (instr, OpCodes.Stloc_1, null);
455                                                 break;
456                                         case 2:
457                                                 Modify (instr, OpCodes.Stloc_2, null);
458                                                 break;
459                                         case 3:
460                                                 Modify (instr, OpCodes.Stloc_3, null);
461                                                 break;
462                                         default:
463                                                 if (index < 256)
464                                                         Modify (instr, OpCodes.Stloc_S, instr.Operand);
465                                                 break;
466                                         }
467                                         break;
468                                 case Code.Ldarga:
469                                         index = m_method.Parameters.IndexOf ((ParameterDefinition) instr.Operand);
470                                         if (index == -1 && instr.Operand == m_method.This)
471                                                 index = 0;
472                                         else if (m_method.HasThis)
473                                                 index++;
474                                         if (index < 256)
475                                                 Modify (instr, OpCodes.Ldarga_S, instr.Operand);
476                                         break;
477                                 case Code.Ldloca:
478                                         if (m_variables.IndexOf ((VariableDefinition) instr.Operand) < 256)
479                                                 Modify (instr, OpCodes.Ldloca_S, instr.Operand);
480                                         break;
481                                 case Code.Ldc_I4:
482                                         int i = (int) instr.Operand;
483                                         switch (i) {
484                                         case -1:
485                                                 Modify (instr, OpCodes.Ldc_I4_M1, null);
486                                                 break;
487                                         case 0:
488                                                 Modify (instr, OpCodes.Ldc_I4_0, null);
489                                                 break;
490                                         case 1:
491                                                 Modify (instr, OpCodes.Ldc_I4_1, null);
492                                                 break;
493                                         case 2:
494                                                 Modify (instr, OpCodes.Ldc_I4_2, null);
495                                                 break;
496                                         case 3:
497                                                 Modify (instr, OpCodes.Ldc_I4_3, null);
498                                                 break;
499                                         case 4:
500                                                 Modify (instr, OpCodes.Ldc_I4_4, null);
501                                                 break;
502                                         case 5:
503                                                 Modify (instr, OpCodes.Ldc_I4_5, null);
504                                                 break;
505                                         case 6:
506                                                 Modify (instr, OpCodes.Ldc_I4_6, null);
507                                                 break;
508                                         case 7:
509                                                 Modify (instr, OpCodes.Ldc_I4_7, null);
510                                                 break;
511                                         case 8:
512                                                 Modify (instr, OpCodes.Ldc_I4_8, null);
513                                                 break;
514                                         default:
515                                                 if (i >= -128 && i < 128)
516                                                         Modify (instr, OpCodes.Ldc_I4_S, (sbyte) i);
517                                                 break;
518                                         }
519                                         break;
520                                 }
521                         }
522
523                         OptimizeBranches ();
524                 }
525
526                 void OptimizeBranches ()
527                 {
528                         ComputeOffsets ();
529
530                         foreach (Instruction instr in m_instructions) {
531                                 if (instr.OpCode.OperandType != OperandType.InlineBrTarget)
532                                         continue;
533
534                                 if (OptimizeBranch (instr))
535                                         ComputeOffsets ();
536                         }
537                 }
538
539                 static bool OptimizeBranch (Instruction instr)
540                 {
541                         int offset = ((Instruction) instr.Operand).Offset - (instr.Offset + instr.OpCode.Size + 4);
542                         if (! (offset >= -128 && offset <= 127))
543                                 return false;
544
545                         switch (instr.OpCode.Code) {
546                         case Code.Br:
547                                 instr.OpCode = OpCodes.Br_S;
548                                 break;
549                         case Code.Brfalse:
550                                 instr.OpCode = OpCodes.Brfalse_S;
551                                 break;
552                         case Code.Brtrue:
553                                 instr.OpCode = OpCodes.Brtrue_S;
554                                 break;
555                         case Code.Beq:
556                                 instr.OpCode = OpCodes.Beq_S;
557                                 break;
558                         case Code.Bge:
559                                 instr.OpCode = OpCodes.Bge_S;
560                                 break;
561                         case Code.Bgt:
562                                 instr.OpCode = OpCodes.Bgt_S;
563                                 break;
564                         case Code.Ble:
565                                 instr.OpCode = OpCodes.Ble_S;
566                                 break;
567                         case Code.Blt:
568                                 instr.OpCode = OpCodes.Blt_S;
569                                 break;
570                         case Code.Bne_Un:
571                                 instr.OpCode = OpCodes.Bne_Un_S;
572                                 break;
573                         case Code.Bge_Un:
574                                 instr.OpCode = OpCodes.Bge_Un_S;
575                                 break;
576                         case Code.Bgt_Un:
577                                 instr.OpCode = OpCodes.Bgt_Un_S;
578                                 break;
579                         case Code.Ble_Un:
580                                 instr.OpCode = OpCodes.Ble_Un_S;
581                                 break;
582                         case Code.Blt_Un:
583                                 instr.OpCode = OpCodes.Blt_Un_S;
584                                 break;
585                         case Code.Leave:
586                                 instr.OpCode = OpCodes.Leave_S;
587                                 break;
588                         }
589
590                         return true;
591                 }
592
593                 void ComputeOffsets ()
594                 {
595                         int offset = 0;
596
597                         foreach (Instruction instr in m_instructions) {
598                                 instr.Offset = offset;
599                                 offset += instr.GetSize ();
600                         }
601                 }
602
603                 static void Modify (Instruction i, OpCode op, object operand)
604                 {
605                         i.OpCode = op;
606                         i.Operand = operand;
607                 }
608
609                 public void Accept (ICodeVisitor visitor)
610                 {
611                         visitor.VisitMethodBody (this);
612                         if (HasVariables)
613                                 m_variables.Accept (visitor);
614                         m_instructions.Accept (visitor);
615                         if (HasExceptionHandlers)
616                                 m_exceptions.Accept (visitor);
617                         if (HasScopes)
618                                 m_scopes.Accept (visitor);
619
620                         visitor.TerminateMethodBody (this);
621                 }
622         }
623 }