Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.ExtLibs/HeuristicLab.Cecil/0.9.5/Mono.Cecil-0.9.5/Mono.Cecil/Mono.Cecil.Cil/CodeWriter.cs

Last change on this file was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 16.9 KB
Line 
1//
2// CodeWriter.cs
3//
4// Author:
5//   Jb Evain (jbevain@gmail.com)
6//
7// Copyright (c) 2008 - 2011 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
29using System;
30using System.Collections.Generic;
31
32using Mono.Collections.Generic;
33
34using Mono.Cecil.Metadata;
35using Mono.Cecil.PE;
36
37using RVA = System.UInt32;
38
39#if !READ_ONLY
40
41namespace Mono.Cecil.Cil {
42
43  sealed class CodeWriter : ByteBuffer {
44
45    readonly RVA code_base;
46    internal readonly MetadataBuilder metadata;
47    readonly Dictionary<uint, MetadataToken> standalone_signatures;
48
49    RVA current;
50    MethodBody body;
51
52    public CodeWriter (MetadataBuilder metadata)
53      : base (0)
54    {
55      this.code_base = metadata.text_map.GetNextRVA (TextSegment.CLIHeader);
56      this.current = code_base;
57      this.metadata = metadata;
58      this.standalone_signatures = new Dictionary<uint, MetadataToken> ();
59    }
60
61    public RVA WriteMethodBody (MethodDefinition method)
62    {
63      var rva = BeginMethod ();
64
65      if (IsUnresolved (method)) {
66        if (method.rva == 0)
67          return 0;
68
69        WriteUnresolvedMethodBody (method);
70      } else {
71        if (IsEmptyMethodBody (method.Body))
72          return 0;
73
74        WriteResolvedMethodBody (method);
75      }
76
77      Align (4);
78
79      EndMethod ();
80      return rva;
81    }
82
83    static bool IsEmptyMethodBody (MethodBody body)
84    {
85      return body.instructions.IsNullOrEmpty ()
86        && body.variables.IsNullOrEmpty ();
87    }
88
89    static bool IsUnresolved (MethodDefinition method)
90    {
91      return method.HasBody && method.HasImage && method.body == null;
92    }
93
94    void WriteUnresolvedMethodBody (MethodDefinition method)
95    {
96      var code_reader = metadata.module.Read (method, (_, reader) => reader.code);
97
98      MethodSymbols symbols;
99      var buffer = code_reader.PatchRawMethodBody (method, this, out symbols);
100
101      WriteBytes (buffer);
102
103      if (symbols.instructions.IsNullOrEmpty ())
104        return;
105
106      symbols.method_token = method.token;
107      symbols.local_var_token = GetLocalVarToken (buffer, symbols);
108
109      var symbol_writer = metadata.symbol_writer;
110      if (symbol_writer != null)
111        symbol_writer.Write (symbols);
112    }
113
114    static MetadataToken GetLocalVarToken (ByteBuffer buffer, MethodSymbols symbols)
115    {
116      if (symbols.variables.IsNullOrEmpty ())
117        return MetadataToken.Zero;
118
119      buffer.position = 8;
120      return new MetadataToken (buffer.ReadUInt32 ());
121    }
122
123    void WriteResolvedMethodBody (MethodDefinition method)
124    {
125      body = method.Body;
126      ComputeHeader ();
127      if (RequiresFatHeader ())
128        WriteFatHeader ();
129      else
130        WriteByte ((byte) (0x2 | (body.CodeSize << 2))); // tiny
131
132      WriteInstructions ();
133
134      if (body.HasExceptionHandlers)
135        WriteExceptionHandlers ();
136
137      var symbol_writer = metadata.symbol_writer;
138      if (symbol_writer != null)
139        symbol_writer.Write (body);
140    }
141
142    void WriteFatHeader ()
143    {
144      var body = this.body;
145      byte flags = 0x3; // fat
146      if (body.InitLocals)
147        flags |= 0x10;  // init locals
148      if (body.HasExceptionHandlers)
149        flags |= 0x8; // more sections
150
151      WriteByte (flags);
152      WriteByte (0x30);
153      WriteInt16 ((short) body.max_stack_size);
154      WriteInt32 (body.code_size);
155      body.local_var_token = body.HasVariables
156        ? GetStandAloneSignature (body.Variables)
157        : MetadataToken.Zero;
158      WriteMetadataToken (body.local_var_token);
159    }
160
161    void WriteInstructions ()
162    {
163      var instructions = body.Instructions;
164      var items = instructions.items;
165      var size = instructions.size;
166
167      for (int i = 0; i < size; i++) {
168        var instruction = items [i];
169        WriteOpCode (instruction.opcode);
170        WriteOperand (instruction);
171      }
172    }
173
174    void WriteOpCode (OpCode opcode)
175    {
176      if (opcode.Size == 1) {
177        WriteByte (opcode.Op2);
178      } else {
179        WriteByte (opcode.Op1);
180        WriteByte (opcode.Op2);
181      }
182    }
183
184    void WriteOperand (Instruction instruction)
185    {
186      var opcode = instruction.opcode;
187      var operand_type = opcode.OperandType;
188      if (operand_type == OperandType.InlineNone)
189        return;
190
191      var operand = instruction.operand;
192      if (operand == null)
193        throw new ArgumentException ();
194
195      switch (operand_type) {
196      case OperandType.InlineSwitch: {
197        var targets = (Instruction []) operand;
198        WriteInt32 (targets.Length);
199        var diff = instruction.Offset + opcode.Size + (4 * (targets.Length + 1));
200        for (int i = 0; i < targets.Length; i++)
201          WriteInt32 (GetTargetOffset (targets [i]) - diff);
202        break;
203      }
204      case OperandType.ShortInlineBrTarget: {
205        var target = (Instruction) operand;
206        WriteSByte ((sbyte) (GetTargetOffset (target) - (instruction.Offset + opcode.Size + 1)));
207        break;
208      }
209      case OperandType.InlineBrTarget: {
210        var target = (Instruction) operand;
211        WriteInt32 (GetTargetOffset (target) - (instruction.Offset + opcode.Size + 4));
212        break;
213      }
214      case OperandType.ShortInlineVar:
215        WriteByte ((byte) GetVariableIndex ((VariableDefinition) operand));
216        break;
217      case OperandType.ShortInlineArg:
218        WriteByte ((byte) GetParameterIndex ((ParameterDefinition) operand));
219        break;
220      case OperandType.InlineVar:
221        WriteInt16 ((short) GetVariableIndex ((VariableDefinition) operand));
222        break;
223      case OperandType.InlineArg:
224        WriteInt16 ((short) GetParameterIndex ((ParameterDefinition) operand));
225        break;
226      case OperandType.InlineSig:
227        WriteMetadataToken (GetStandAloneSignature ((CallSite) operand));
228        break;
229      case OperandType.ShortInlineI:
230        if (opcode == OpCodes.Ldc_I4_S)
231          WriteSByte ((sbyte) operand);
232        else
233          WriteByte ((byte) operand);
234        break;
235      case OperandType.InlineI:
236        WriteInt32 ((int) operand);
237        break;
238      case OperandType.InlineI8:
239        WriteInt64 ((long) operand);
240        break;
241      case OperandType.ShortInlineR:
242        WriteSingle ((float) operand);
243        break;
244      case OperandType.InlineR:
245        WriteDouble ((double) operand);
246        break;
247      case OperandType.InlineString:
248        WriteMetadataToken (
249          new MetadataToken (
250            TokenType.String,
251            GetUserStringIndex ((string) operand)));
252        break;
253      case OperandType.InlineType:
254      case OperandType.InlineField:
255      case OperandType.InlineMethod:
256      case OperandType.InlineTok:
257        WriteMetadataToken (metadata.LookupToken ((IMetadataTokenProvider) operand));
258        break;
259      default:
260        throw new ArgumentException ();
261      }
262    }
263
264    int GetTargetOffset (Instruction instruction)
265    {
266      if (instruction == null) {
267        var last = body.instructions [body.instructions.size - 1];
268        return last.offset + last.GetSize ();
269      }
270
271      return instruction.offset;
272    }
273
274    uint GetUserStringIndex (string @string)
275    {
276      if (@string == null)
277        return 0;
278
279      return metadata.user_string_heap.GetStringIndex (@string);
280    }
281
282    static int GetVariableIndex (VariableDefinition variable)
283    {
284      return variable.Index;
285    }
286
287    int GetParameterIndex (ParameterDefinition parameter)
288    {
289      if (body.method.HasThis) {
290        if (parameter == body.this_parameter)
291          return 0;
292
293        return parameter.Index + 1;
294      }
295
296      return parameter.Index;
297    }
298
299    bool RequiresFatHeader ()
300    {
301      var body = this.body;
302      return body.CodeSize >= 64
303        || body.InitLocals
304        || body.HasVariables
305        || body.HasExceptionHandlers
306        || body.MaxStackSize > 8;
307    }
308
309    void ComputeHeader ()
310    {
311      int offset = 0;
312      var instructions = body.instructions;
313      var items = instructions.items;
314      var count = instructions.size;
315      var stack_size = 0;
316      var max_stack = 0;
317      Dictionary<Instruction, int> stack_sizes = null;
318
319      if (body.HasExceptionHandlers)
320        ComputeExceptionHandlerStackSize (ref stack_sizes);
321
322      for (int i = 0; i < count; i++) {
323        var instruction = items [i];
324        instruction.offset = offset;
325        offset += instruction.GetSize ();
326
327        ComputeStackSize (instruction, ref stack_sizes, ref stack_size, ref max_stack);
328      }
329
330      body.code_size = offset;
331      body.max_stack_size = max_stack;
332    }
333
334    void ComputeExceptionHandlerStackSize (ref Dictionary<Instruction, int> stack_sizes)
335    {
336      var exception_handlers = body.ExceptionHandlers;
337
338      for (int i = 0; i < exception_handlers.Count; i++) {
339        var exception_handler = exception_handlers [i];
340
341        switch (exception_handler.HandlerType) {
342        case ExceptionHandlerType.Catch:
343          AddExceptionStackSize (exception_handler.HandlerStart, ref stack_sizes);
344          break;
345        case ExceptionHandlerType.Filter:
346          AddExceptionStackSize (exception_handler.FilterStart, ref stack_sizes);
347          AddExceptionStackSize (exception_handler.HandlerStart, ref stack_sizes);
348          break;
349        }
350      }
351    }
352
353    static void AddExceptionStackSize (Instruction handler_start, ref Dictionary<Instruction, int> stack_sizes)
354    {
355      if (handler_start == null)
356        return;
357
358      if (stack_sizes == null)
359        stack_sizes = new Dictionary<Instruction, int> ();
360
361      stack_sizes [handler_start] = 1;
362    }
363
364    static void ComputeStackSize (Instruction instruction, ref Dictionary<Instruction, int> stack_sizes, ref int stack_size, ref int max_stack)
365    {
366      int computed_size;
367      if (stack_sizes != null && stack_sizes.TryGetValue (instruction, out computed_size))
368        stack_size = computed_size;
369
370      max_stack = System.Math.Max (max_stack, stack_size);
371      ComputeStackDelta (instruction, ref stack_size);
372      max_stack = System.Math.Max (max_stack, stack_size);
373
374      CopyBranchStackSize (instruction, ref stack_sizes, stack_size);
375      ComputeStackSize (instruction, ref stack_size);
376    }
377
378    static void CopyBranchStackSize (Instruction instruction, ref Dictionary<Instruction, int> stack_sizes, int stack_size)
379    {
380      if (stack_size == 0)
381        return;
382
383      switch (instruction.opcode.OperandType) {
384      case OperandType.ShortInlineBrTarget:
385      case OperandType.InlineBrTarget:
386        CopyBranchStackSize (ref stack_sizes, (Instruction) instruction.operand, stack_size);
387        break;
388      case OperandType.InlineSwitch:
389        var targets = (Instruction[]) instruction.operand;
390        for (int i = 0; i < targets.Length; i++)
391          CopyBranchStackSize (ref stack_sizes, targets [i], stack_size);
392        break;
393      }
394    }
395
396    static void CopyBranchStackSize (ref Dictionary<Instruction, int> stack_sizes, Instruction target, int stack_size)
397    {
398      if (stack_sizes == null)
399        stack_sizes = new Dictionary<Instruction, int> ();
400
401      int branch_stack_size = stack_size;
402
403      int computed_size;
404      if (stack_sizes.TryGetValue (target, out computed_size))
405        branch_stack_size = System.Math.Max (branch_stack_size, computed_size);
406
407      stack_sizes [target] = branch_stack_size;
408    }
409
410    static void ComputeStackSize (Instruction instruction, ref int stack_size)
411    {
412      switch (instruction.opcode.FlowControl) {
413      case FlowControl.Branch:
414      case FlowControl.Break:
415      case FlowControl.Throw:
416      case FlowControl.Return:
417        stack_size = 0;
418        break;
419      }
420    }
421
422    static void ComputeStackDelta (Instruction instruction, ref int stack_size)
423    {
424      switch (instruction.opcode.FlowControl) {
425      case FlowControl.Call: {
426        var method = (IMethodSignature) instruction.operand;
427        stack_size -= (method.HasParameters ? method.Parameters.Count : 0)
428          + (method.HasThis && instruction.opcode.Code != Code.Newobj ? 1 : 0);
429        stack_size += (method.ReturnType.etype == ElementType.Void ? 0 : 1)
430          + (method.HasThis && instruction.opcode.Code == Code.Newobj ? 1 : 0);
431        break;
432      }
433      default:
434        ComputePopDelta (instruction.opcode.StackBehaviourPop, ref stack_size);
435        ComputePushDelta (instruction.opcode.StackBehaviourPush, ref stack_size);
436        break;
437      }
438    }
439
440    static void ComputePopDelta (StackBehaviour pop_behavior, ref int stack_size)
441    {
442      switch (pop_behavior) {
443      case StackBehaviour.Popi:
444      case StackBehaviour.Popref:
445      case StackBehaviour.Pop1:
446        stack_size--;
447        break;
448      case StackBehaviour.Pop1_pop1:
449      case StackBehaviour.Popi_pop1:
450      case StackBehaviour.Popi_popi:
451      case StackBehaviour.Popi_popi8:
452      case StackBehaviour.Popi_popr4:
453      case StackBehaviour.Popi_popr8:
454      case StackBehaviour.Popref_pop1:
455      case StackBehaviour.Popref_popi:
456        stack_size -= 2;
457        break;
458      case StackBehaviour.Popi_popi_popi:
459      case StackBehaviour.Popref_popi_popi:
460      case StackBehaviour.Popref_popi_popi8:
461      case StackBehaviour.Popref_popi_popr4:
462      case StackBehaviour.Popref_popi_popr8:
463      case StackBehaviour.Popref_popi_popref:
464        stack_size -= 3;
465        break;
466      case StackBehaviour.PopAll:
467        stack_size = 0;
468        break;
469      }
470    }
471
472    static void ComputePushDelta (StackBehaviour push_behaviour, ref int stack_size)
473    {
474      switch (push_behaviour) {
475      case StackBehaviour.Push1:
476      case StackBehaviour.Pushi:
477      case StackBehaviour.Pushi8:
478      case StackBehaviour.Pushr4:
479      case StackBehaviour.Pushr8:
480      case StackBehaviour.Pushref:
481        stack_size++;
482        break;
483      case StackBehaviour.Push1_push1:
484        stack_size += 2;
485        break;
486      }
487    }
488
489    void WriteExceptionHandlers ()
490    {
491      Align (4);
492
493      var handlers = body.ExceptionHandlers;
494
495      if (handlers.Count < 0x15 && !RequiresFatSection (handlers))
496        WriteSmallSection (handlers);
497      else
498        WriteFatSection (handlers);
499    }
500
501    static bool RequiresFatSection (Collection<ExceptionHandler> handlers)
502    {
503      for (int i = 0; i < handlers.Count; i++) {
504        var handler = handlers [i];
505
506        if (IsFatRange (handler.TryStart, handler.TryEnd))
507          return true;
508
509        if (IsFatRange (handler.HandlerStart, handler.HandlerEnd))
510          return true;
511
512        if (handler.HandlerType == ExceptionHandlerType.Filter
513          && IsFatRange (handler.FilterStart, handler.HandlerStart))
514          return true;
515      }
516
517      return false;
518    }
519
520    static bool IsFatRange (Instruction start, Instruction end)
521    {
522      if (end == null)
523        return true;
524
525      return end.Offset - start.Offset > 255 || start.Offset > 65535;
526    }
527
528    void WriteSmallSection (Collection<ExceptionHandler> handlers)
529    {
530      const byte eh_table = 0x1;
531
532      WriteByte (eh_table);
533      WriteByte ((byte) (handlers.Count * 12 + 4));
534      WriteBytes (2);
535
536      WriteExceptionHandlers (
537        handlers,
538        i => WriteUInt16 ((ushort) i),
539        i => WriteByte ((byte) i));
540    }
541
542    void WriteFatSection (Collection<ExceptionHandler> handlers)
543    {
544      const byte eh_table = 0x1;
545      const byte fat_format = 0x40;
546
547      WriteByte (eh_table | fat_format);
548
549      int size = handlers.Count * 24 + 4;
550      WriteByte ((byte) (size & 0xff));
551      WriteByte ((byte) ((size >> 8) & 0xff));
552      WriteByte ((byte) ((size >> 16) & 0xff));
553
554      WriteExceptionHandlers (handlers, WriteInt32, WriteInt32);
555    }
556
557    void WriteExceptionHandlers (Collection<ExceptionHandler> handlers, Action<int> write_entry, Action<int> write_length)
558    {
559      for (int i = 0; i < handlers.Count; i++) {
560        var handler = handlers [i];
561
562        write_entry ((int) handler.HandlerType);
563
564        write_entry (handler.TryStart.Offset);
565        write_length (GetTargetOffset (handler.TryEnd) - handler.TryStart.Offset);
566
567        write_entry (handler.HandlerStart.Offset);
568        write_length (GetTargetOffset (handler.HandlerEnd) - handler.HandlerStart.Offset);
569
570        WriteExceptionHandlerSpecific (handler);
571      }
572    }
573
574    void WriteExceptionHandlerSpecific (ExceptionHandler handler)
575    {
576      switch (handler.HandlerType) {
577      case ExceptionHandlerType.Catch:
578        WriteMetadataToken (metadata.LookupToken (handler.CatchType));
579        break;
580      case ExceptionHandlerType.Filter:
581        WriteInt32 (handler.FilterStart.Offset);
582        break;
583      default:
584        WriteInt32 (0);
585        break;
586      }
587    }
588
589    public MetadataToken GetStandAloneSignature (Collection<VariableDefinition> variables)
590    {
591      var signature = metadata.GetLocalVariableBlobIndex (variables);
592
593      return GetStandAloneSignatureToken (signature);
594    }
595
596    public MetadataToken GetStandAloneSignature (CallSite call_site)
597    {
598      var signature = metadata.GetCallSiteBlobIndex (call_site);
599      var token = GetStandAloneSignatureToken (signature);
600      call_site.MetadataToken = token;
601      return token;
602    }
603
604    MetadataToken GetStandAloneSignatureToken (uint signature)
605    {
606      MetadataToken token;
607      if (standalone_signatures.TryGetValue (signature, out token))
608        return token;
609
610      token = new MetadataToken (TokenType.Signature, metadata.AddStandAloneSignature (signature));
611      standalone_signatures.Add (signature, token);
612      return token;
613    }
614
615    RVA BeginMethod ()
616    {
617      return current;
618    }
619
620    void WriteMetadataToken (MetadataToken token)
621    {
622      WriteUInt32 (token.ToUInt32 ());
623    }
624
625    void Align (int align)
626    {
627      align--;
628      WriteBytes (((position + align) & ~align) - position);
629    }
630
631    void EndMethod ()
632    {
633      current = (RVA) (code_base + position);
634    }
635  }
636}
637
638#endif
Note: See TracBrowser for help on using the repository browser.