update Mono.Cecil
[mono.git] / mcs / class / Mono.Cecil / Mono.Cecil.Cil / CodeWriter.cs
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
29 using System;
30 using System.Collections.Generic;
31
32 using Mono.Collections.Generic;
33
34 using Mono.Cecil.Metadata;
35 using Mono.Cecil.PE;
36
37 using RVA = System.UInt32;
38
39 #if !READ_ONLY
40
41 namespace 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