System.Drawing: added email to icon and test file headers
[mono.git] / mcs / class / IKVM.Reflection / Emit / ILGenerator.cs
1 /*
2   Copyright (C) 2008-2011 Jeroen Frijters
3
4   This software is provided 'as-is', without any express or implied
5   warranty.  In no event will the authors be held liable for any damages
6   arising from the use of this software.
7
8   Permission is granted to anyone to use this software for any purpose,
9   including commercial applications, and to alter it and redistribute it
10   freely, subject to the following restrictions:
11
12   1. The origin of this software must not be misrepresented; you must not
13      claim that you wrote the original software. If you use this software
14      in a product, an acknowledgment in the product documentation would be
15      appreciated but is not required.
16   2. Altered source versions must be plainly marked as such, and must not be
17      misrepresented as being the original software.
18   3. This notice may not be removed or altered from any source distribution.
19
20   Jeroen Frijters
21   jeroen@frijters.net
22   
23 */
24 using System;
25 using System.Runtime.InteropServices;
26 using System.Collections.Generic;
27 using System.Diagnostics.SymbolStore;
28 using System.Diagnostics;
29 using IKVM.Reflection.Metadata;
30 using IKVM.Reflection.Writer;
31
32 namespace IKVM.Reflection.Emit
33 {
34         public struct Label
35         {
36                 // 1-based here, to make sure that an uninitialized Label isn't valid
37                 private readonly int index1;
38
39                 internal Label(int index)
40                 {
41                         this.index1 = index + 1;
42                 }
43
44                 internal int Index
45                 {
46                         get { return index1 - 1; }
47                 }
48
49                 public bool Equals(Label other)
50                 {
51                         return other.index1 == index1;
52                 }
53
54                 public override bool Equals(object obj)
55                 {
56                         return this == obj as Label?;
57                 }
58
59                 public override int GetHashCode()
60                 {
61                         return index1;
62                 }
63
64                 public static bool operator ==(Label arg1, Label arg2)
65                 {
66                         return arg1.index1 == arg2.index1;
67                 }
68
69                 public static bool operator !=(Label arg1, Label arg2)
70                 {
71                         return !(arg1 == arg2);
72                 }
73         }
74
75         public sealed class LocalBuilder
76         {
77                 private readonly Type localType;
78                 private readonly int index;
79                 private readonly bool pinned;
80                 internal string name;
81                 internal int startOffset;
82                 internal int endOffset;
83
84                 internal LocalBuilder(Type localType, int index, bool pinned)
85                 {
86                         this.localType = localType;
87                         this.index = index;
88                         this.pinned = pinned;
89                 }
90
91                 public void SetLocalSymInfo(string name)
92                 {
93                         this.name = name;
94                 }
95
96                 public void SetLocalSymInfo(string name, int startOffset, int endOffset)
97                 {
98                         this.name = name;
99                         this.startOffset = startOffset;
100                         this.endOffset = endOffset;
101                 }
102
103                 public Type LocalType
104                 {
105                         get { return localType; }
106                 }
107
108                 public int LocalIndex
109                 {
110                         get { return index; }
111                 }
112
113                 public bool IsPinned
114                 {
115                         get { return pinned; }
116                 }
117         }
118
119         sealed class MarkerType : Type
120         {
121                 public override Type BaseType
122                 {
123                         get { throw new InvalidOperationException(); }
124                 }
125
126                 public override TypeAttributes Attributes
127                 {
128                         get { throw new InvalidOperationException(); }
129                 }
130
131                 public override string Name
132                 {
133                         get { throw new InvalidOperationException(); }
134                 }
135
136                 public override string FullName
137                 {
138                         get { throw new InvalidOperationException(); }
139                 }
140
141                 public override Module Module
142                 {
143                         get { throw new InvalidOperationException(); }
144                 }
145         }
146
147         public sealed class ILGenerator
148         {
149                 private static readonly Type FAULT = new MarkerType();
150                 private static readonly Type FINALLY = new MarkerType();
151                 private static readonly Type FILTER = new MarkerType();
152                 private readonly ModuleBuilder moduleBuilder;
153                 private readonly ByteBuffer code;
154                 private readonly List<LocalBuilder> locals = new List<LocalBuilder>();
155                 private List<CustomModifiers> localCustomModifiers;
156                 private readonly List<int> tokenFixups = new List<int>();
157                 private readonly List<int> labels = new List<int>();
158                 private readonly List<int> labelStackHeight = new List<int>();
159                 private readonly List<LabelFixup> labelFixups = new List<LabelFixup>();
160                 private readonly List<SequencePoint> sequencePoints = new List<SequencePoint>();
161                 private readonly List<ExceptionBlock> exceptions = new List<ExceptionBlock>();
162                 private readonly Stack<ExceptionBlock> exceptionStack = new Stack<ExceptionBlock>();
163                 private ushort maxStack;
164                 private bool fatHeader;
165                 private int stackHeight;
166                 private Scope scope;
167                 private byte exceptionBlockAssistanceMode = EBAM_COMPAT;
168                 private const byte EBAM_COMPAT = 0;
169                 private const byte EBAM_DISABLE = 1;
170                 private const byte EBAM_CLEVER = 2;
171
172                 private struct LabelFixup
173                 {
174                         internal int label;
175                         internal int offset;
176                 }
177
178                 private sealed class ExceptionBlock : IComparer<ExceptionBlock>
179                 {
180                         internal readonly int ordinal;
181                         internal Label labelEnd;
182                         internal int tryOffset;
183                         internal int tryLength;
184                         internal int handlerOffset;
185                         internal int handlerLength;
186                         internal Type exceptionType;    // FINALLY = finally block, FILTER = handler with filter, FAULT = fault block
187                         internal int filterOffset;
188
189                         internal ExceptionBlock(int ordinal)
190                         {
191                                 this.ordinal = ordinal;
192                         }
193
194                         int IComparer<ExceptionBlock>.Compare(ExceptionBlock x, ExceptionBlock y)
195                         {
196                                 // Mono's sort insists on doing unnecessary comparisons
197                                 if (x == y)
198                                 {
199                                         return 0;
200                                 }
201                                 else if (x.tryOffset == y.tryOffset && x.tryLength == y.tryLength)
202                                 {
203                                         return x.ordinal < y.ordinal ? -1 : 1;
204                                 }
205                                 else if (x.tryOffset >= y.tryOffset && x.handlerOffset + x.handlerLength <= y.handlerOffset + y.handlerLength)
206                                 {
207                                         return -1;
208                                 }
209                                 else if (y.tryOffset >= x.tryOffset && y.handlerOffset + y.handlerLength <= x.handlerOffset + x.handlerLength)
210                                 {
211                                         return 1;
212                                 }
213                                 else
214                                 {
215                                         return x.ordinal < y.ordinal ? -1 : 1;
216                                 }
217                         }
218                 }
219
220                 private struct SequencePoint
221                 {
222                         internal ISymbolDocumentWriter document;
223                         internal int offset;
224                         internal int startLine;
225                         internal int startColumn;
226                         internal int endLine;
227                         internal int endColumn;
228                 }
229
230                 private sealed class Scope
231                 {
232                         internal readonly Scope parent;
233                         internal readonly List<Scope> children = new List<Scope>();
234                         internal readonly List<LocalBuilder> locals = new List<LocalBuilder>();
235                         internal int startOffset;
236                         internal int endOffset;
237
238                         internal Scope(Scope parent)
239                         {
240                                 this.parent = parent;
241                         }
242                 }
243
244                 internal ILGenerator(ModuleBuilder moduleBuilder, int initialCapacity)
245                 {
246                         this.code = new ByteBuffer(initialCapacity);
247                         this.moduleBuilder = moduleBuilder;
248                         if (moduleBuilder.symbolWriter != null)
249                         {
250                                 scope = new Scope(null);
251                         }
252                 }
253
254                 // non-standard API
255                 public void __DisableExceptionBlockAssistance()
256                 {
257                         exceptionBlockAssistanceMode = EBAM_DISABLE;
258                 }
259
260                 // non-standard API
261                 public void __CleverExceptionBlockAssistance()
262                 {
263                         exceptionBlockAssistanceMode = EBAM_CLEVER;
264                 }
265
266                 // non-standard API
267                 public int __MaxStackSize
268                 {
269                         get { return maxStack; }
270                         set
271                         {
272                                 maxStack = (ushort)value;
273                                 fatHeader = true;
274                         }
275                 }
276
277                 // non-standard API
278                 // returns -1 if the current position is currently unreachable
279                 public int __StackHeight
280                 {
281                         get { return stackHeight; }
282                 }
283
284                 // new in .NET 4.0
285                 public int ILOffset
286                 {
287                         get { return code.Position; }
288                 }
289
290                 public void BeginCatchBlock(Type exceptionType)
291                 {
292                         ExceptionBlock block = exceptionStack.Peek();
293                         if (exceptionBlockAssistanceMode == EBAM_COMPAT || (exceptionBlockAssistanceMode == EBAM_CLEVER && stackHeight != -1))
294                         {
295                                 if (exceptionType == null)
296                                 {
297                                         Emit(OpCodes.Endfilter);
298                                 }
299                                 else
300                                 {
301                                         Emit(OpCodes.Leave, block.labelEnd);
302                                 }
303                         }
304                         stackHeight = 0;
305                         UpdateStack(1);
306                         if (exceptionType == null)
307                         {
308                                 if (block.exceptionType != FILTER || block.handlerOffset != 0)
309                                 {
310                                         throw new ArgumentNullException("exceptionType");
311                                 }
312                                 block.handlerOffset = code.Position;
313                         }
314                         else
315                         {
316                                 if (block.tryLength == 0)
317                                 {
318                                         block.tryLength = code.Position - block.tryOffset;
319                                 }
320                                 else
321                                 {
322                                         block.handlerLength = code.Position - block.handlerOffset;
323                                         exceptionStack.Pop();
324                                         ExceptionBlock newBlock = new ExceptionBlock(exceptions.Count);
325                                         newBlock.labelEnd = block.labelEnd;
326                                         newBlock.tryOffset = block.tryOffset;
327                                         newBlock.tryLength = block.tryLength;
328                                         block = newBlock;
329                                         exceptions.Add(block);
330                                         exceptionStack.Push(block);
331                                 }
332                                 block.exceptionType = exceptionType;
333                                 if (exceptionType == FILTER)
334                                 {
335                                         block.filterOffset = code.Position;
336                                 }
337                                 else
338                                 {
339                                         block.handlerOffset = code.Position;
340                                 }
341                         }
342                 }
343
344                 public Label BeginExceptionBlock()
345                 {
346                         ExceptionBlock block = new ExceptionBlock(exceptions.Count);
347                         block.labelEnd = DefineLabel();
348                         block.tryOffset = code.Position;
349                         exceptionStack.Push(block);
350                         exceptions.Add(block);
351                         stackHeight = 0;
352                         return block.labelEnd;
353                 }
354
355                 public void BeginExceptFilterBlock()
356                 {
357                         BeginCatchBlock(FILTER);
358                 }
359
360                 public void BeginFaultBlock()
361                 {
362                         BeginFinallyFaultBlock(FAULT);
363                 }
364
365                 public void BeginFinallyBlock()
366                 {
367                         BeginFinallyFaultBlock(FINALLY);
368                 }
369
370                 private void BeginFinallyFaultBlock(Type type)
371                 {
372                         ExceptionBlock block = exceptionStack.Peek();
373                         if (exceptionBlockAssistanceMode == EBAM_COMPAT || (exceptionBlockAssistanceMode == EBAM_CLEVER && stackHeight != -1))
374                         {
375                                 Emit(OpCodes.Leave, block.labelEnd);
376                         }
377                         if (block.handlerOffset == 0)
378                         {
379                                 block.tryLength = code.Position - block.tryOffset;
380                         }
381                         else
382                         {
383                                 block.handlerLength = code.Position - block.handlerOffset;
384                                 Label labelEnd;
385                                 if (exceptionBlockAssistanceMode != EBAM_COMPAT)
386                                 {
387                                         labelEnd = block.labelEnd;
388                                 }
389                                 else
390                                 {
391                                         MarkLabel(block.labelEnd);
392                                         labelEnd = DefineLabel();
393                                         Emit(OpCodes.Leave, labelEnd);
394                                 }
395                                 exceptionStack.Pop();
396                                 ExceptionBlock newBlock = new ExceptionBlock(exceptions.Count);
397                                 newBlock.labelEnd = labelEnd;
398                                 newBlock.tryOffset = block.tryOffset;
399                                 newBlock.tryLength = code.Position - block.tryOffset;
400                                 block = newBlock;
401                                 exceptions.Add(block);
402                                 exceptionStack.Push(block);
403                         }
404                         block.handlerOffset = code.Position;
405                         block.exceptionType = type;
406                         stackHeight = 0;
407                 }
408
409                 public void EndExceptionBlock()
410                 {
411                         ExceptionBlock block = exceptionStack.Pop();
412                         if (exceptionBlockAssistanceMode == EBAM_COMPAT || (exceptionBlockAssistanceMode == EBAM_CLEVER && stackHeight != -1))
413                         {
414                                 if (block.filterOffset != 0 || (block.exceptionType != FINALLY && block.exceptionType != FAULT))
415                                 {
416                                         Emit(OpCodes.Leave, block.labelEnd);
417                                 }
418                                 else
419                                 {
420                                         Emit(OpCodes.Endfinally);
421                                 }
422                         }
423                         MarkLabel(block.labelEnd);
424                         block.handlerLength = code.Position - block.handlerOffset;
425                 }
426
427                 public void BeginScope()
428                 {
429                         Scope newScope = new Scope(scope);
430                         scope.children.Add(newScope);
431                         scope = newScope;
432                         scope.startOffset = code.Position;
433                 }
434
435                 public void UsingNamespace(string usingNamespace)
436                 {
437                         if (moduleBuilder.symbolWriter != null)
438                         {
439                                 moduleBuilder.symbolWriter.UsingNamespace(usingNamespace);
440                         }
441                 }
442
443                 public LocalBuilder DeclareLocal(Type localType)
444                 {
445                         return DeclareLocal(localType, false);
446                 }
447
448                 public LocalBuilder DeclareLocal(Type localType, bool pinned)
449                 {
450                         LocalBuilder local = new LocalBuilder(localType, locals.Count, pinned);
451                         locals.Add(local);
452                         if (scope != null)
453                         {
454                                 scope.locals.Add(local);
455                         }
456                         return local;
457                 }
458
459                 public LocalBuilder __DeclareLocal(Type localType, bool pinned, CustomModifiers customModifiers)
460                 {
461                         if (!customModifiers.IsEmpty)
462                         {
463                                 if (localCustomModifiers == null)
464                                 {
465                                         localCustomModifiers = new List<CustomModifiers>();
466                                 }
467                                 // we lazily fill up the list (to sync with the locals list) and we don't need to
468                                 // make sure that the list has the same length as the locals list, because
469                                 // Signature.WriteLocalVarSig() can tolerate that.
470                                 while (localCustomModifiers.Count < locals.Count)
471                                 {
472                                         localCustomModifiers.Add(new CustomModifiers());
473                                 }
474                                 localCustomModifiers.Add(customModifiers);
475                         }
476                         return DeclareLocal(localType, pinned);
477                 }
478
479                 public Label DefineLabel()
480                 {
481                         Label label = new Label(labels.Count);
482                         labels.Add(-1);
483                         labelStackHeight.Add(-1);
484                         return label;
485                 }
486
487                 public void Emit(OpCode opc)
488                 {
489                         Debug.Assert(opc != OpCodes.Ret || (opc == OpCodes.Ret && stackHeight <= 1));
490                         if (opc.Value < 0)
491                         {
492                                 code.Write((byte)(opc.Value >> 8));
493                         }
494                         code.Write((byte)opc.Value);
495                         switch (opc.FlowControl)
496                         {
497                                 case FlowControl.Branch:
498                                 case FlowControl.Break:
499                                 case FlowControl.Return:
500                                 case FlowControl.Throw:
501                                         stackHeight = -1;
502                                         break;
503                                 default:
504                                         UpdateStack(opc.StackDiff);
505                                         break;
506                         }
507                 }
508
509                 private void UpdateStack(int stackdiff)
510                 {
511                         if (stackHeight == -1)
512                         {
513                                 // we're about to emit code that is either unreachable or reachable only via a backward branch
514                                 stackHeight = 0;
515                         }
516                         Debug.Assert(stackHeight >= 0 && stackHeight <= ushort.MaxValue);
517                         stackHeight += stackdiff;
518                         Debug.Assert(stackHeight >= 0 && stackHeight <= ushort.MaxValue);
519                         maxStack = Math.Max(maxStack, (ushort)stackHeight);
520                 }
521
522                 public void Emit(OpCode opc, byte arg)
523                 {
524                         Emit(opc);
525                         code.Write(arg);
526                 }
527
528                 public void Emit(OpCode opc, double arg)
529                 {
530                         Emit(opc);
531                         code.Write(arg);
532                 }
533
534                 public void Emit(OpCode opc, FieldInfo field)
535                 {
536                         Emit(opc);
537                         WriteToken(moduleBuilder.GetFieldToken(field));
538                 }
539
540                 public void Emit(OpCode opc, short arg)
541                 {
542                         Emit(opc);
543                         code.Write(arg);
544                 }
545
546                 public void Emit(OpCode opc, int arg)
547                 {
548                         Emit(opc);
549                         code.Write(arg);
550                 }
551
552                 public void Emit(OpCode opc, long arg)
553                 {
554                         Emit(opc);
555                         code.Write(arg);
556                 }
557
558                 public void Emit(OpCode opc, Label label)
559                 {
560                         // We need special stackHeight handling for unconditional branches,
561                         // because the branch and next flows have differing stack heights.
562                         // Note that this assumes that unconditional branches do not push/pop.
563                         int flowStackHeight = this.stackHeight;
564                         Emit(opc);
565                         if (opc == OpCodes.Leave || opc == OpCodes.Leave_S)
566                         {
567                                 flowStackHeight = 0;
568                         }
569                         else if (opc.FlowControl != FlowControl.Branch)
570                         {
571                                 flowStackHeight = this.stackHeight;
572                         }
573                         // if the label has already been marked, we can emit the branch offset directly
574                         if (labels[label.Index] != -1)
575                         {
576                                 if (labelStackHeight[label.Index] != flowStackHeight && (labelStackHeight[label.Index] != 0 || flowStackHeight != -1))
577                                 {
578                                         // the "backward branch constraint" prohibits this, so we don't need to support it
579                                         throw new NotSupportedException("'Backward branch constraints' violated");
580                                 }
581                                 if (opc.OperandType == OperandType.ShortInlineBrTarget)
582                                 {
583                                         WriteByteBranchOffset(labels[label.Index] - (code.Position + 1));
584                                 }
585                                 else
586                                 {
587                                         code.Write(labels[label.Index] - (code.Position + 4));
588                                 }
589                         }
590                         else
591                         {
592                                 Debug.Assert(labelStackHeight[label.Index] == -1 || labelStackHeight[label.Index] == flowStackHeight || (flowStackHeight == -1 && labelStackHeight[label.Index] == 0));
593                                 labelStackHeight[label.Index] = flowStackHeight;
594                                 LabelFixup fix = new LabelFixup();
595                                 fix.label = label.Index;
596                                 fix.offset = code.Position;
597                                 labelFixups.Add(fix);
598                                 if (opc.OperandType == OperandType.ShortInlineBrTarget)
599                                 {
600                                         code.Write((byte)1);
601                                 }
602                                 else
603                                 {
604                                         code.Write(4);
605                                 }
606                         }
607                 }
608
609                 private void WriteByteBranchOffset(int offset)
610                 {
611                         if (offset < -128 || offset > 127)
612                         {
613                                 throw new NotSupportedException("Branch offset of " + offset + " does not fit in one-byte branch target at position " + code.Position);
614                         }
615                         code.Write((byte)offset);
616                 }
617
618                 public void Emit(OpCode opc, Label[] labels)
619                 {
620                         Emit(opc);
621                         LabelFixup fix = new LabelFixup();
622                         fix.label = -1;
623                         fix.offset = code.Position;
624                         labelFixups.Add(fix);
625                         code.Write(labels.Length);
626                         foreach (Label label in labels)
627                         {
628                                 code.Write(label.Index);
629                                 if (this.labels[label.Index] != -1)
630                                 {
631                                         if (labelStackHeight[label.Index] != stackHeight)
632                                         {
633                                                 // the "backward branch constraint" prohibits this, so we don't need to support it
634                                                 throw new NotSupportedException();
635                                         }
636                                 }
637                                 else
638                                 {
639                                         Debug.Assert(labelStackHeight[label.Index] == -1 || labelStackHeight[label.Index] == stackHeight);
640                                         labelStackHeight[label.Index] = stackHeight;
641                                 }
642                         }
643                 }
644
645                 public void Emit(OpCode opc, LocalBuilder local)
646                 {
647                         if ((opc == OpCodes.Ldloc || opc == OpCodes.Ldloca || opc == OpCodes.Stloc) && local.LocalIndex < 256)
648                         {
649                                 if (opc == OpCodes.Ldloc)
650                                 {
651                                         switch (local.LocalIndex)
652                                         {
653                                                 case 0:
654                                                         Emit(OpCodes.Ldloc_0);
655                                                         break;
656                                                 case 1:
657                                                         Emit(OpCodes.Ldloc_1);
658                                                         break;
659                                                 case 2:
660                                                         Emit(OpCodes.Ldloc_2);
661                                                         break;
662                                                 case 3:
663                                                         Emit(OpCodes.Ldloc_3);
664                                                         break;
665                                                 default:
666                                                         Emit(OpCodes.Ldloc_S);
667                                                         code.Write((byte)local.LocalIndex);
668                                                         break;
669                                         }
670                                 }
671                                 else if (opc == OpCodes.Ldloca)
672                                 {
673                                         Emit(OpCodes.Ldloca_S);
674                                         code.Write((byte)local.LocalIndex);
675                                 }
676                                 else if (opc == OpCodes.Stloc)
677                                 {
678                                         switch (local.LocalIndex)
679                                         {
680                                                 case 0:
681                                                         Emit(OpCodes.Stloc_0);
682                                                         break;
683                                                 case 1:
684                                                         Emit(OpCodes.Stloc_1);
685                                                         break;
686                                                 case 2:
687                                                         Emit(OpCodes.Stloc_2);
688                                                         break;
689                                                 case 3:
690                                                         Emit(OpCodes.Stloc_3);
691                                                         break;
692                                                 default:
693                                                         Emit(OpCodes.Stloc_S);
694                                                         code.Write((byte)local.LocalIndex);
695                                                         break;
696                                         }
697                                 }
698                         }
699                         else
700                         {
701                                 Emit(opc);
702                                 switch (opc.OperandType)
703                                 {
704                                         case OperandType.InlineVar:
705                                                 code.Write((ushort)local.LocalIndex);
706                                                 break;
707                                         case OperandType.ShortInlineVar:
708                                                 code.Write((byte)local.LocalIndex);
709                                                 break;
710                                 }
711                         }
712                 }
713
714                 private void WriteToken(FieldToken token)
715                 {
716                         if (token.IsPseudoToken)
717                         {
718                                 tokenFixups.Add(code.Position);
719                         }
720                         code.Write(token.Token);
721                 }
722
723                 private void WriteToken(MethodToken token)
724                 {
725                         if (token.IsPseudoToken)
726                         {
727                                 tokenFixups.Add(code.Position);
728                         }
729                         code.Write(token.Token);
730                 }
731
732                 private void UpdateStack(OpCode opc, bool hasthis, Type returnType, int parameterCount)
733                 {
734                         if (opc == OpCodes.Jmp)
735                         {
736                                 stackHeight = -1;
737                         }
738                         else if (opc.FlowControl == FlowControl.Call)
739                         {
740                                 int stackdiff = 0;
741                                 if ((hasthis && opc != OpCodes.Newobj) || opc == OpCodes.Calli)
742                                 {
743                                         // pop this
744                                         stackdiff--;
745                                 }
746                                 // pop parameters
747                                 stackdiff -= parameterCount;
748                                 if (returnType != moduleBuilder.universe.System_Void)
749                                 {
750                                         // push return value
751                                         stackdiff++;
752                                 }
753                                 UpdateStack(stackdiff);
754                         }
755                 }
756
757                 public void Emit(OpCode opc, MethodInfo method)
758                 {
759                         UpdateStack(opc, method.HasThis, method.ReturnType, method.ParameterCount);
760                         Emit(opc);
761                         WriteToken(moduleBuilder.GetMethodTokenForIL(method));
762                 }
763
764                 public void Emit(OpCode opc, ConstructorInfo constructor)
765                 {
766                         Emit(opc, constructor.GetMethodInfo());
767                 }
768
769                 public void Emit(OpCode opc, sbyte arg)
770                 {
771                         Emit(opc);
772                         code.Write(arg);
773                 }
774
775                 public void Emit(OpCode opc, float arg)
776                 {
777                         Emit(opc);
778                         code.Write(arg);
779                 }
780
781                 public void Emit(OpCode opc, string str)
782                 {
783                         Emit(opc);
784                         code.Write(0x70000000 | moduleBuilder.UserStrings.Add(str));
785                 }
786
787                 public void Emit(OpCode opc, Type type)
788                 {
789                         Emit(opc);
790                         if (opc == OpCodes.Ldtoken)
791                         {
792                                 code.Write(moduleBuilder.GetTypeToken(type).Token);
793                         }
794                         else
795                         {
796                                 code.Write(moduleBuilder.GetTypeTokenForMemberRef(type));
797                         }
798                 }
799
800                 public void Emit(OpCode opcode, SignatureHelper signature)
801                 {
802                         Emit(opcode);
803                         UpdateStack(opcode, signature.HasThis, signature.ReturnType, signature.ParameterCount);
804                         code.Write(0x11000000 | moduleBuilder.StandAloneSig.FindOrAddRecord(moduleBuilder.Blobs.Add(signature.GetSignature(moduleBuilder))));
805                 }
806
807                 public void EmitCall(OpCode opc, MethodInfo method, Type[] optionalParameterTypes)
808                 {
809                         __EmitCall(opc, method, optionalParameterTypes, null);
810                 }
811
812                 public void __EmitCall(OpCode opc, MethodInfo method, Type[] optionalParameterTypes, CustomModifiers[] customModifiers)
813                 {
814                         if (optionalParameterTypes == null || optionalParameterTypes.Length == 0)
815                         {
816                                 Emit(opc, method);
817                         }
818                         else
819                         {
820                                 Emit(opc);
821                                 UpdateStack(opc, method.HasThis, method.ReturnType, method.ParameterCount + optionalParameterTypes.Length);
822                                 ByteBuffer sig = new ByteBuffer(16);
823                                 method.MethodSignature.WriteMethodRefSig(moduleBuilder, sig, optionalParameterTypes, customModifiers);
824                                 MemberRefTable.Record record = new MemberRefTable.Record();
825                                 if (method.Module == moduleBuilder)
826                                 {
827                                         record.Class = method.MetadataToken;
828                                 }
829                                 else
830                                 {
831                                         record.Class = moduleBuilder.GetTypeTokenForMemberRef(method.DeclaringType ?? method.Module.GetModuleType());
832                                 }
833                                 record.Name = moduleBuilder.Strings.Add(method.Name);
834                                 record.Signature = moduleBuilder.Blobs.Add(sig);
835                                 code.Write(0x0A000000 | moduleBuilder.MemberRef.FindOrAddRecord(record));
836                         }
837                 }
838
839                 public void __EmitCall(OpCode opc, ConstructorInfo constructor, Type[] optionalParameterTypes)
840                 {
841                         EmitCall(opc, constructor.GetMethodInfo(), optionalParameterTypes);
842                 }
843
844                 public void __EmitCall(OpCode opc, ConstructorInfo constructor, Type[] optionalParameterTypes, CustomModifiers[] customModifiers)
845                 {
846                         __EmitCall(opc, constructor.GetMethodInfo(), optionalParameterTypes, customModifiers);
847                 }
848
849                 public void EmitCalli(OpCode opc, CallingConvention callingConvention, Type returnType, Type[] parameterTypes)
850                 {
851                         __EmitCalli(opc, moduleBuilder.universe.MakeStandAloneMethodSig(callingConvention, returnType, new CustomModifiers(), parameterTypes, null));
852                 }
853
854                 public void EmitCalli(OpCode opc, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type[] optionalParameterTypes)
855                 {
856                         __EmitCalli(opc, moduleBuilder.universe.MakeStandAloneMethodSig(callingConvention, returnType, new CustomModifiers(), parameterTypes, optionalParameterTypes, null));
857                 }
858
859                 public void __EmitCalli(OpCode opc, __StandAloneMethodSig sig)
860                 {
861                         Emit(opc);
862                         if (sig.IsUnmanaged)
863                         {
864                                 UpdateStack(opc, false, sig.ReturnType, sig.ParameterCount);
865                         }
866                         else
867                         {
868                                 CallingConventions callingConvention = sig.CallingConvention;
869                                 UpdateStack(opc, (callingConvention & CallingConventions.HasThis | CallingConventions.ExplicitThis) == CallingConventions.HasThis, sig.ReturnType, sig.ParameterCount);
870                         }
871                         ByteBuffer bb = new ByteBuffer(16);
872                         Signature.WriteStandAloneMethodSig(moduleBuilder, bb, sig);
873                         code.Write(0x11000000 | moduleBuilder.StandAloneSig.FindOrAddRecord(moduleBuilder.Blobs.Add(bb)));
874                 }
875
876                 public void EmitWriteLine(string text)
877                 {
878                         Universe u = moduleBuilder.universe;
879                         Emit(OpCodes.Ldstr, text);
880                         Emit(OpCodes.Call, u.Import(typeof(Console)).GetMethod("WriteLine", new Type[] { u.System_String }));
881                 }
882
883                 public void EmitWriteLine(FieldInfo field)
884                 {
885                         Universe u = moduleBuilder.universe;
886                         Emit(OpCodes.Call, u.Import(typeof(Console)).GetMethod("get_Out"));
887                         if (field.IsStatic)
888                         {
889                                 Emit(OpCodes.Ldsfld, field);
890                         }
891                         else
892                         {
893                                 Emit(OpCodes.Ldarg_0);
894                                 Emit(OpCodes.Ldfld, field);
895                         }
896                         Emit(OpCodes.Callvirt, u.Import(typeof(System.IO.TextWriter)).GetMethod("WriteLine", new Type[] { field.FieldType }));
897                 }
898
899                 public void EmitWriteLine(LocalBuilder local)
900                 {
901                         Universe u = moduleBuilder.universe;
902                         Emit(OpCodes.Call, u.Import(typeof(Console)).GetMethod("get_Out"));
903                         Emit(OpCodes.Ldloc, local);
904                         Emit(OpCodes.Callvirt, u.Import(typeof(System.IO.TextWriter)).GetMethod("WriteLine", new Type[] { local.LocalType }));
905                 }
906
907                 public void EndScope()
908                 {
909                         scope.endOffset = code.Position;
910                         scope = scope.parent;
911                 }
912
913                 public void MarkLabel(Label loc)
914                 {
915                         Debug.Assert(stackHeight == -1 || labelStackHeight[loc.Index] == -1 || stackHeight == labelStackHeight[loc.Index]);
916                         labels[loc.Index] = code.Position;
917                         if (labelStackHeight[loc.Index] == -1)
918                         {
919                                 if (stackHeight == -1)
920                                 {
921                                         // We're at a location that can only be reached by a backward branch,
922                                         // so according to the "backward branch constraint" that must mean the stack is empty,
923                                         // but note that this may be an unused label followed by another label that is used and
924                                         // that does have a non-zero stack height, so we don't yet set stackHeight here.
925                                         labelStackHeight[loc.Index] = 0;
926                                 }
927                                 else
928                                 {
929                                         labelStackHeight[loc.Index] = stackHeight;
930                                 }
931                         }
932                         else
933                         {
934                                 Debug.Assert(stackHeight == -1 || stackHeight == labelStackHeight[loc.Index]);
935                                 stackHeight = labelStackHeight[loc.Index];
936                         }
937                 }
938
939                 public void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn)
940                 {
941                         SequencePoint sp = new SequencePoint();
942                         sp.document = document;
943                         sp.offset = code.Position;
944                         sp.startLine = startLine;
945                         sp.startColumn = startColumn;
946                         sp.endLine = endLine;
947                         sp.endColumn = endColumn;
948                         sequencePoints.Add(sp);
949                 }
950
951                 public void ThrowException(Type excType)
952                 {
953                         Emit(OpCodes.Newobj, excType.GetConstructor(Type.EmptyTypes));
954                         Emit(OpCodes.Throw);
955                 }
956
957                 internal int WriteBody(bool initLocals)
958                 {
959                         if (moduleBuilder.symbolWriter != null)
960                         {
961                                 Debug.Assert(scope != null && scope.parent == null);
962                                 scope.endOffset = code.Position;
963                         }
964
965                         ResolveBranches();
966
967                         ByteBuffer bb = moduleBuilder.methodBodies;
968
969                         int localVarSigTok = 0;
970
971                         int rva;
972                         if (locals.Count == 0 && exceptions.Count == 0 && maxStack <= 8 && code.Length < 64 && !fatHeader)
973                         {
974                                 rva = WriteTinyHeaderAndCode(bb);
975                         }
976                         else
977                         {
978                                 rva = WriteFatHeaderAndCode(bb, ref localVarSigTok, initLocals);
979                         }
980
981                         if (moduleBuilder.symbolWriter != null)
982                         {
983                                 if (sequencePoints.Count != 0)
984                                 {
985                                         ISymbolDocumentWriter document = sequencePoints[0].document;
986                                         int[] offsets = new int[sequencePoints.Count];
987                                         int[] lines = new int[sequencePoints.Count];
988                                         int[] columns = new int[sequencePoints.Count];
989                                         int[] endLines = new int[sequencePoints.Count];
990                                         int[] endColumns = new int[sequencePoints.Count];
991                                         for (int i = 0; i < sequencePoints.Count; i++)
992                                         {
993                                                 if (sequencePoints[i].document != document)
994                                                 {
995                                                         throw new NotImplementedException();
996                                                 }
997                                                 offsets[i] = sequencePoints[i].offset;
998                                                 lines[i] = sequencePoints[i].startLine;
999                                                 columns[i] = sequencePoints[i].startColumn;
1000                                                 endLines[i] = sequencePoints[i].endLine;
1001                                                 endColumns[i] = sequencePoints[i].endColumn;
1002                                         }
1003                                         moduleBuilder.symbolWriter.DefineSequencePoints(document, offsets, lines, columns, endLines, endColumns);
1004                                 }
1005
1006                                 WriteScope(scope, localVarSigTok);
1007                         }
1008                         return rva;
1009                 }
1010
1011                 private void ResolveBranches()
1012                 {
1013                         foreach (LabelFixup fixup in labelFixups)
1014                         {
1015                                 // is it a switch?
1016                                 if (fixup.label == -1)
1017                                 {
1018                                         code.Position = fixup.offset;
1019                                         int count = code.GetInt32AtCurrentPosition();
1020                                         int offset = fixup.offset + 4 + 4 * count;
1021                                         code.Position += 4;
1022                                         for (int i = 0; i < count; i++)
1023                                         {
1024                                                 int index = code.GetInt32AtCurrentPosition();
1025                                                 code.Write(labels[index] - offset);
1026                                         }
1027                                 }
1028                                 else
1029                                 {
1030                                         code.Position = fixup.offset;
1031                                         byte size = code.GetByteAtCurrentPosition();
1032                                         int branchOffset = labels[fixup.label] - (code.Position + size);
1033                                         if (size == 1)
1034                                         {
1035                                                 WriteByteBranchOffset(branchOffset);
1036                                         }
1037                                         else
1038                                         {
1039                                                 code.Write(branchOffset);
1040                                         }
1041                                 }
1042                         }
1043                 }
1044
1045                 private int WriteTinyHeaderAndCode(ByteBuffer bb)
1046                 {
1047                         int rva = bb.Position;
1048                         const byte CorILMethod_TinyFormat = 0x2;
1049                         bb.Write((byte)(CorILMethod_TinyFormat | (code.Length << 2)));
1050                         WriteCode(bb);
1051                         return rva;
1052                 }
1053
1054                 private int WriteFatHeaderAndCode(ByteBuffer bb, ref int localVarSigTok, bool initLocals)
1055                 {
1056                         // fat headers require 4-byte alignment
1057                         bb.Align(4);
1058                         int rva = bb.Position;
1059
1060                         if (locals.Count != 0)
1061                         {
1062                                 ByteBuffer localVarSig = new ByteBuffer(locals.Count + 2);
1063                                 Signature.WriteLocalVarSig(moduleBuilder, localVarSig, locals, localCustomModifiers);
1064                                 localVarSigTok = 0x11000000 | moduleBuilder.StandAloneSig.FindOrAddRecord(moduleBuilder.Blobs.Add(localVarSig));
1065                         }
1066
1067                         const byte CorILMethod_FatFormat = 0x03;
1068                         const byte CorILMethod_MoreSects = 0x08;
1069                         const byte CorILMethod_InitLocals = 0x10;
1070
1071                         short flagsAndSize = (short)(CorILMethod_FatFormat | (3 << 12));
1072                         if (initLocals)
1073                         {
1074                                 flagsAndSize |= CorILMethod_InitLocals;
1075                         }
1076
1077                         if (exceptions.Count > 0)
1078                         {
1079                                 flagsAndSize |= CorILMethod_MoreSects;
1080                         }
1081
1082                         bb.Write(flagsAndSize);
1083                         bb.Write(maxStack);
1084                         bb.Write(code.Length);
1085                         bb.Write(localVarSigTok);
1086
1087                         WriteCode(bb);
1088
1089                         if (exceptions.Count > 0)
1090                         {
1091                                 bb.Align(4);
1092
1093                                 bool fat = false;
1094                                 foreach (ExceptionBlock block in exceptions)
1095                                 {
1096                                         if (block.tryOffset > 65535 || block.tryLength > 255 || block.handlerOffset > 65535 || block.handlerLength > 255)
1097                                         {
1098                                                 fat = true;
1099                                                 break;
1100                                         }
1101                                 }
1102                                 exceptions.Sort(exceptions[0]);
1103                                 if (exceptions.Count * 12 + 4 > 255)
1104                                 {
1105                                         fat = true;
1106                                 }
1107                                 const byte CorILMethod_Sect_EHTable = 0x1;
1108                                 const byte CorILMethod_Sect_FatFormat = 0x40;
1109                                 const short COR_ILEXCEPTION_CLAUSE_EXCEPTION = 0x0000;
1110                                 const short COR_ILEXCEPTION_CLAUSE_FILTER = 0x0001;
1111                                 const short COR_ILEXCEPTION_CLAUSE_FINALLY = 0x0002;
1112                                 const short COR_ILEXCEPTION_CLAUSE_FAULT = 0x0004;
1113
1114                                 if (fat)
1115                                 {
1116                                         bb.Write((byte)(CorILMethod_Sect_EHTable | CorILMethod_Sect_FatFormat));
1117                                         int dataSize = exceptions.Count * 24 + 4;
1118                                         bb.Write((byte)dataSize);
1119                                         bb.Write((short)(dataSize >> 8));
1120                                         foreach (ExceptionBlock block in exceptions)
1121                                         {
1122                                                 if (block.exceptionType == FAULT)
1123                                                 {
1124                                                         bb.Write((int)COR_ILEXCEPTION_CLAUSE_FAULT);
1125                                                 }
1126                                                 else if (block.exceptionType == FILTER)
1127                                                 {
1128                                                         bb.Write((int)COR_ILEXCEPTION_CLAUSE_FILTER);
1129                                                 }
1130                                                 else if (block.exceptionType == FINALLY)
1131                                                 {
1132                                                         bb.Write((int)COR_ILEXCEPTION_CLAUSE_FINALLY);
1133                                                 }
1134                                                 else
1135                                                 {
1136                                                         bb.Write((int)COR_ILEXCEPTION_CLAUSE_EXCEPTION);
1137                                                 }
1138                                                 bb.Write(block.tryOffset);
1139                                                 bb.Write(block.tryLength);
1140                                                 bb.Write(block.handlerOffset);
1141                                                 bb.Write(block.handlerLength);
1142                                                 if (block.exceptionType != FAULT && block.exceptionType != FILTER && block.exceptionType != FINALLY)
1143                                                 {
1144                                                         bb.Write(moduleBuilder.GetTypeTokenForMemberRef(block.exceptionType));
1145                                                 }
1146                                                 else
1147                                                 {
1148                                                         bb.Write(block.filterOffset);
1149                                                 }
1150                                         }
1151                                 }
1152                                 else
1153                                 {
1154                                         bb.Write(CorILMethod_Sect_EHTable);
1155                                         bb.Write((byte)(exceptions.Count * 12 + 4));
1156                                         bb.Write((short)0);
1157                                         foreach (ExceptionBlock block in exceptions)
1158                                         {
1159                                                 if (block.exceptionType == FAULT)
1160                                                 {
1161                                                         bb.Write(COR_ILEXCEPTION_CLAUSE_FAULT);
1162                                                 }
1163                                                 else if (block.exceptionType == FILTER)
1164                                                 {
1165                                                         bb.Write(COR_ILEXCEPTION_CLAUSE_FILTER);
1166                                                 }
1167                                                 else if (block.exceptionType == FINALLY)
1168                                                 {
1169                                                         bb.Write(COR_ILEXCEPTION_CLAUSE_FINALLY);
1170                                                 }
1171                                                 else
1172                                                 {
1173                                                         bb.Write(COR_ILEXCEPTION_CLAUSE_EXCEPTION);
1174                                                 }
1175                                                 bb.Write((short)block.tryOffset);
1176                                                 bb.Write((byte)block.tryLength);
1177                                                 bb.Write((short)block.handlerOffset);
1178                                                 bb.Write((byte)block.handlerLength);
1179                                                 if (block.exceptionType != FAULT && block.exceptionType != FILTER && block.exceptionType != FINALLY)
1180                                                 {
1181                                                         bb.Write(moduleBuilder.GetTypeTokenForMemberRef(block.exceptionType));
1182                                                 }
1183                                                 else
1184                                                 {
1185                                                         bb.Write(block.filterOffset);
1186                                                 }
1187                                         }
1188                                 }
1189                         }
1190                         return rva;
1191                 }
1192
1193                 private void WriteCode(ByteBuffer bb)
1194                 {
1195                         int codeOffset = bb.Position;
1196                         foreach (int fixup in this.tokenFixups)
1197                         {
1198                                 moduleBuilder.tokenFixupOffsets.Add(fixup + codeOffset);
1199                         }
1200                         bb.Write(code);
1201                 }
1202
1203                 private void WriteScope(Scope scope, int localVarSigTok)
1204                 {
1205                         moduleBuilder.symbolWriter.OpenScope(scope.startOffset);
1206                         foreach (LocalBuilder local in scope.locals)
1207                         {
1208                                 if (local.name != null)
1209                                 {
1210                                         int startOffset = local.startOffset;
1211                                         int endOffset = local.endOffset;
1212                                         if (startOffset == 0 && endOffset == 0)
1213                                         {
1214                                                 startOffset = scope.startOffset;
1215                                                 endOffset = scope.endOffset;
1216                                         }
1217                                         moduleBuilder.symbolWriter.DefineLocalVariable2(local.name, 0, localVarSigTok, SymAddressKind.ILOffset, local.LocalIndex, 0, 0, startOffset, endOffset);
1218                                 }
1219                         }
1220                         foreach (Scope child in scope.children)
1221                         {
1222                                 WriteScope(child, localVarSigTok);
1223                         }
1224                         moduleBuilder.symbolWriter.CloseScope(scope.endOffset);
1225                 }
1226         }
1227 }