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