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