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