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