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