merge -r 53370:58178
[mono.git] / mcs / class / corlib / System.Reflection.Emit / ILGenerator.cs
1
2 //
3 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
12 // 
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 // 
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24
25 //
26 // System.Reflection.Emit/ILGenerator.cs
27 //
28 // Author:
29 //   Paolo Molaro (lupus@ximian.com)
30 //
31 // (C) 2001 Ximian, Inc.  http://www.ximian.com
32 //
33
34 using System;
35 using System.Collections;
36 using System.Diagnostics.SymbolStore;
37 using System.Runtime.InteropServices;
38
39 namespace System.Reflection.Emit {
40
41         internal struct ILExceptionBlock {
42                 public const int CATCH = 0;
43                 public const int FILTER = 1;
44                 public const int FINALLY = 2;
45                 public const int FAULT = 4;
46
47                 internal Type extype;
48                 internal int type;
49                 internal int start;
50                 internal int len;
51                 internal int filter_offset;
52                 
53                 internal void Debug () {
54 #if NO
55                         System.Console.Write ("\ttype="+type.ToString()+" start="+start.ToString()+" len="+len.ToString());
56                         if (extype != null)
57                                 System.Console.WriteLine (" extype="+extype.ToString());
58                         else
59                                 System.Console.WriteLine ("");
60 #endif
61                 }
62         }
63         internal struct ILExceptionInfo {
64                 ILExceptionBlock[] handlers;
65                 internal int start;
66                 int len;
67                 internal Label end;
68
69                 internal int NumHandlers ()
70                 {
71                         return handlers.Length;
72                 }
73                 
74                 internal void AddCatch (Type extype, int offset)
75                 {
76                         int i;
77                         End (offset);
78                         add_block (offset);
79                         i = handlers.Length - 1;
80                         handlers [i].type = ILExceptionBlock.CATCH;
81                         handlers [i].start = offset;
82                         handlers [i].extype = extype;
83                 }
84
85                 internal void AddFinally (int offset)
86                 {
87                         int i;
88                         End (offset);
89                         add_block (offset);
90                         i = handlers.Length - 1;
91                         handlers [i].type = ILExceptionBlock.FINALLY;
92                         handlers [i].start = offset;
93                         handlers [i].extype = null;
94                 }
95
96                 internal void AddFault (int offset)
97                 {
98                         int i;
99                         End (offset);
100                         add_block (offset);
101                         i = handlers.Length - 1;
102                         handlers [i].type = ILExceptionBlock.FAULT;
103                         handlers [i].start = offset;
104                         handlers [i].extype = null;
105                 }
106
107                 internal void AddFilter (int offset)
108                 {
109                         int i;
110                         End (offset);
111                         add_block (offset);
112                         i = handlers.Length - 1;
113                         handlers [i].type = ILExceptionBlock.FILTER;
114                         handlers [i].extype = null;
115                         handlers [i].filter_offset = offset;
116                 }
117
118                 internal void End (int offset)
119                 {
120                         if (handlers == null)
121                                 return;
122                         int i = handlers.Length - 1;
123                         if (i >= 0)
124                                 handlers [i].len = offset - handlers [i].start;
125                 }
126
127                 internal int LastClauseType ()
128                 {
129                         if (handlers != null)
130                                 return handlers [handlers.Length-1].type;
131                         else
132                                 return ILExceptionBlock.CATCH;
133                 }
134
135                 internal void PatchLastClauseStart (int start)
136                 {
137                         if (handlers != null && handlers.Length > 0)
138                                 handlers [handlers.Length - 1].start = start;
139                 }
140
141                 internal void Debug (int b)
142                 {
143 #if NO
144                         System.Console.WriteLine ("Handler {0} at {1}, len: {2}", b, start, len);
145                         for (int i = 0; i < handlers.Length; ++i)
146                                 handlers [i].Debug ();
147 #endif
148                 }
149
150                 void add_block (int offset)
151                 {
152                         if (handlers != null) {
153                                 int i = handlers.Length;
154                                 ILExceptionBlock[] new_b = new ILExceptionBlock [i + 1];
155                                 System.Array.Copy (handlers, new_b, i);
156                                 handlers = new_b;
157                                 handlers [i].len = offset - handlers [i].start;
158                         } else {
159                                 handlers = new ILExceptionBlock [1];
160                                 len = offset - start;
161                         }
162                 }
163         }
164         
165         internal struct ILTokenInfo {
166                 public MemberInfo member;
167                 public int code_pos;
168         }
169
170         internal interface TokenGenerator {
171                 int GetToken (string str);
172
173                 int GetToken (MemberInfo member);
174
175                 int GetToken (MethodInfo method, Type[] opt_param_types);
176
177                 int GetToken (SignatureHelper helper);
178         }               
179
180 #if NET_2_0
181         [ComVisible (true)]
182 #endif
183         [ClassInterface (ClassInterfaceType.None)]
184         public class ILGenerator: _ILGenerator {
185                 private struct LabelFixup {
186                         public int offset;    // The number of bytes between pos and the
187                                                               // offset of the jump
188                         public int pos;       // Where offset of the label is placed
189                         public int label_idx; // The label to jump to
190                 };
191                 
192                 struct LabelData {
193                         public LabelData (int addr, int maxStack)
194                         {
195                                 this.addr = addr;
196                                 this.maxStack = maxStack;
197                         }
198                         
199                         public int addr;
200                         public int maxStack; 
201                 }
202                 
203                 static readonly Type void_type = typeof (void);
204                 #region Sync with reflection.h
205                 private byte[] code;
206                 private int code_len;
207                 private int max_stack;
208                 private int cur_stack;
209                 private LocalBuilder[] locals;
210                 private ILExceptionInfo[] ex_handlers;
211                 private int num_token_fixups;
212                 private ILTokenInfo[] token_fixups;
213                 #endregion
214                 
215                 private LabelData [] labels;
216                 private int num_labels;
217                 private LabelFixup[] fixups;
218                 private int num_fixups;
219                 internal Module module;
220                 private Stack scopes;
221                 private int cur_block;
222                 private Stack open_blocks;
223                 private TokenGenerator token_gen;
224                 
225                 const int defaultFixupSize = 8;
226                 const int defaultLabelsSize = 8;
227                 ArrayList sequencePointLists;
228                 SequencePointList currentSequence;
229
230                 internal ILGenerator (Module m, TokenGenerator token_gen, int size)
231                 {
232                         if (size < 0)
233                                 size = 128;
234                         code_len = 0;
235                         code = new byte [size];
236                         cur_stack = max_stack = 0;
237                         num_fixups = num_labels = 0;
238                         token_fixups = new ILTokenInfo [8];
239                         num_token_fixups = 0;
240                         module = m;
241                         open_blocks = new Stack ();
242                         this.token_gen = token_gen;
243                 }
244                 
245                 private void add_token_fixup (MemberInfo mi)
246                 {
247                         if (num_token_fixups == token_fixups.Length) {
248                                 ILTokenInfo[] ntf = new ILTokenInfo [num_token_fixups * 2];
249                                 token_fixups.CopyTo (ntf, 0);
250                                 token_fixups = ntf;
251                         }
252                         token_fixups [num_token_fixups].member = mi;
253                         token_fixups [num_token_fixups++].code_pos = code_len;
254                 }
255
256                 private void make_room (int nbytes)
257                 {
258                         if (code_len + nbytes < code.Length)
259                                 return;
260                         byte[] new_code = new byte [(code_len + nbytes) * 2 + 128];
261                         System.Array.Copy (code, 0, new_code, 0, code.Length);
262                         code = new_code;
263                 }
264
265                 private void emit_int (int val)
266                 {
267                         code [code_len++] = (byte) (val & 0xFF);
268                         code [code_len++] = (byte) ((val >> 8) & 0xFF);
269                         code [code_len++] = (byte) ((val >> 16) & 0xFF);
270                         code [code_len++] = (byte) ((val >> 24) & 0xFF);
271                 }
272
273                 /* change to pass by ref to avoid copy */
274                 private void ll_emit (OpCode opcode)
275                 {
276                         /* 
277                          * there is already enough room allocated in code.
278                          */
279                         // access op1 and op2 directly since the Value property is useless
280                         if (opcode.Size == 2)
281                                 code [code_len++] = opcode.op1;
282                         code [code_len++] = opcode.op2;
283                         /*
284                          * We should probably keep track of stack needs here.
285                          * Or we may want to run the verifier on the code before saving it
286                          * (this may be needed anyway when the ILGenerator is not used...).
287                          */
288                         switch (opcode.StackBehaviourPush) {
289                         case StackBehaviour.Push1:
290                         case StackBehaviour.Pushi:
291                         case StackBehaviour.Pushi8:
292                         case StackBehaviour.Pushr4:
293                         case StackBehaviour.Pushr8:
294                         case StackBehaviour.Pushref:
295                         case StackBehaviour.Varpush: /* again we are conservative and assume it pushes 1 */
296                                 cur_stack ++;
297                                 break;
298                         case StackBehaviour.Push1_push1:
299                                 cur_stack += 2;
300                                 break;
301                         }
302                         if (max_stack < cur_stack)
303                                 max_stack = cur_stack;
304
305                         /* 
306                          * Note that we adjust for the pop behaviour _after_ setting max_stack.
307                          */
308                         switch (opcode.StackBehaviourPop) {
309                         case StackBehaviour.Varpop:
310                                 break; /* we are conservative and assume it doesn't decrease the stack needs */
311                         case StackBehaviour.Pop1:
312                         case StackBehaviour.Popi:
313                         case StackBehaviour.Popref:
314                                 cur_stack --;
315                                 break;
316                         case StackBehaviour.Pop1_pop1:
317                         case StackBehaviour.Popi_pop1:
318                         case StackBehaviour.Popi_popi:
319                         case StackBehaviour.Popi_popi8:
320                         case StackBehaviour.Popi_popr4:
321                         case StackBehaviour.Popi_popr8:
322                         case StackBehaviour.Popref_pop1:
323                         case StackBehaviour.Popref_popi:
324                                 cur_stack -= 2;
325                                 break;
326                         case StackBehaviour.Popi_popi_popi:
327                         case StackBehaviour.Popref_popi_popi:
328                         case StackBehaviour.Popref_popi_popi8:
329                         case StackBehaviour.Popref_popi_popr4:
330                         case StackBehaviour.Popref_popi_popr8:
331                         case StackBehaviour.Popref_popi_popref:
332                                 cur_stack -= 3;
333                                 break;
334                         }
335                 }
336
337                 private static int target_len (OpCode opcode)
338                 {
339                         if (opcode.OperandType == OperandType.InlineBrTarget)
340                                 return 4;
341                         return 1;
342                 }
343
344                 private void InternalEndClause ()
345                 {
346                         switch (ex_handlers [cur_block].LastClauseType ()) {
347                         case ILExceptionBlock.CATCH:
348                         case ILExceptionBlock.FILTER:
349                                 // how could we optimize code size here?
350                                 Emit (OpCodes.Leave, ex_handlers [cur_block].end);
351                                 break;
352                         case ILExceptionBlock.FAULT:
353                         case ILExceptionBlock.FINALLY:
354                                 Emit (OpCodes.Endfinally);
355                                 break;
356                         }
357                 }
358
359                 public virtual void BeginCatchBlock (Type exceptionType)
360                 {
361                         if (open_blocks.Count <= 0)
362                                 throw new NotSupportedException ("Not in an exception block");
363
364                         if (ex_handlers [cur_block].LastClauseType () == ILExceptionBlock.FILTER) {
365                                 if (exceptionType != null)
366                                         throw new ArgumentException ("Do not supply an exception type for filter clause");
367                                 Emit (OpCodes.Endfilter);
368                                 ex_handlers [cur_block].PatchLastClauseStart (code_len);
369                         } else {
370                                 InternalEndClause ();
371                                 ex_handlers [cur_block].AddCatch (exceptionType, code_len);
372                         }
373                         
374                         cur_stack = 1; // the exception object is on the stack by default
375                         if (max_stack < cur_stack)
376                                 max_stack = cur_stack;
377
378                         //System.Console.WriteLine ("Begin catch Block: {0} {1}",exceptionType.ToString(), max_stack);
379                 }
380
381                 public virtual void BeginExceptFilterBlock ()
382                 {
383                         if (open_blocks.Count <= 0)
384                                 throw new NotSupportedException ("Not in an exception block");
385                         InternalEndClause ();
386
387                         ex_handlers [cur_block].AddFilter (code_len);
388                 }
389
390                 public virtual Label BeginExceptionBlock ()
391                 {
392                         //System.Console.WriteLine ("Begin Block");
393                         
394                         if (ex_handlers != null) {
395                                 cur_block = ex_handlers.Length;
396                                 ILExceptionInfo[] new_ex = new ILExceptionInfo [cur_block + 1];
397                                 System.Array.Copy (ex_handlers, new_ex, cur_block);
398                                 ex_handlers = new_ex;
399                         } else {
400                                 ex_handlers = new ILExceptionInfo [1];
401                                 cur_block = 0;
402                         }
403                         open_blocks.Push (cur_block);
404                         ex_handlers [cur_block].start = code_len;
405                         return ex_handlers [cur_block].end = DefineLabel ();
406                 }
407
408                 public virtual void BeginFaultBlock()
409                 {
410                         if (open_blocks.Count <= 0)
411                                 throw new NotSupportedException ("Not in an exception block");
412                         InternalEndClause ();
413                         //System.Console.WriteLine ("Begin fault Block");
414                         ex_handlers [cur_block].AddFault (code_len);
415                 }
416                 
417                 public virtual void BeginFinallyBlock()
418                 {
419                         if (open_blocks.Count <= 0)
420                                 throw new NotSupportedException ("Not in an exception block");
421                         InternalEndClause ();
422                         //System.Console.WriteLine ("Begin finally Block");
423                         ex_handlers [cur_block].AddFinally (code_len);
424                 }
425                 
426                 public virtual void BeginScope ()
427                 { }
428                 
429                 public LocalBuilder DeclareLocal (Type localType)
430                 {
431                         return DeclareLocal (localType, false);
432                 }
433
434
435 #if NET_2_0
436                 public
437 #else
438                 internal
439 #endif
440                 LocalBuilder DeclareLocal (Type localType, bool pinned)
441                 {
442                         if (localType == null)
443                                 throw new ArgumentNullException ("localType");
444
445                         LocalBuilder res = new LocalBuilder (localType, this);
446                         res.is_pinned = pinned;
447                         
448                         if (locals != null) {
449                                 LocalBuilder[] new_l = new LocalBuilder [locals.Length + 1];
450                                 System.Array.Copy (locals, new_l, locals.Length);
451                                 new_l [locals.Length] = res;
452                                 locals = new_l;
453                         } else {
454                                 locals = new LocalBuilder [1];
455                                 locals [0] = res;
456                         }
457                         res.position = (ushort)(locals.Length - 1);
458                         return res;
459                 }
460                 
461                 public virtual Label DefineLabel ()
462                 {
463                         if (labels == null)
464                                 labels = new LabelData [defaultLabelsSize];
465                         else if (num_labels >= labels.Length) {
466                                 LabelData [] t = new LabelData [labels.Length * 2];
467                                 Array.Copy (labels, t, labels.Length);
468                                 labels = t;
469                         }
470                         
471                         labels [num_labels] = new LabelData (-1, 0);
472                         
473                         return new Label (num_labels++);
474                 }
475                 
476                 public virtual void Emit (OpCode opcode)
477                 {
478                         make_room (2);
479                         ll_emit (opcode);
480                 }
481                 
482                 public virtual void Emit (OpCode opcode, Byte val)
483                 {
484                         make_room (3);
485                         ll_emit (opcode);
486                         code [code_len++] = val;
487                 }
488                 
489 #if NET_2_0
490                 [ComVisible (true)]
491 #endif
492                 public virtual void Emit (OpCode opcode, ConstructorInfo constructor)
493                 {
494                         int token = token_gen.GetToken (constructor);
495                         make_room (6);
496                         ll_emit (opcode);
497                         if (constructor.DeclaringType.Module == module)
498                                 add_token_fixup (constructor);
499                         emit_int (token);
500                         
501                         if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
502                                 cur_stack -= constructor.GetParameterCount ();
503                 }
504                 
505                 public virtual void Emit (OpCode opcode, double val)
506                 {
507                         Double.AssertEndianity (out val);
508
509                         byte[] s = System.BitConverter.GetBytes (val);
510                         make_room (10);
511                         ll_emit (opcode);
512                         if (BitConverter.IsLittleEndian){
513                                 System.Array.Copy (s, 0, code, code_len, 8);
514                                 code_len += 8;
515                         } else {
516                                 code [code_len++] = s [7];
517                                 code [code_len++] = s [6];
518                                 code [code_len++] = s [5];
519                                 code [code_len++] = s [4];
520                                 code [code_len++] = s [3];
521                                 code [code_len++] = s [2];
522                                 code [code_len++] = s [1];
523                                 code [code_len++] = s [0];                              
524                         }
525                 }
526                 
527                 public virtual void Emit (OpCode opcode, FieldInfo field)
528                 {
529                         int token = token_gen.GetToken (field);
530                         make_room (6);
531                         ll_emit (opcode);
532                         if (field.DeclaringType.Module == module)
533                                 add_token_fixup (field);
534                         emit_int (token);
535                 }
536                 
537                 public virtual void Emit (OpCode opcode, Int16 val)
538                 {
539                         make_room (4);
540                         ll_emit (opcode);
541                         code [code_len++] = (byte) (val & 0xFF);
542                         code [code_len++] = (byte) ((val >> 8) & 0xFF);
543                 }
544                 
545                 public virtual void Emit (OpCode opcode, int val)
546                 {
547                         make_room (6);
548                         ll_emit (opcode);
549                         emit_int (val);
550                 }
551                 
552                 public virtual void Emit (OpCode opcode, long val)
553                 {
554                         make_room (10);
555                         ll_emit (opcode);
556                         code [code_len++] = (byte) (val & 0xFF);
557                         code [code_len++] = (byte) ((val >> 8) & 0xFF);
558                         code [code_len++] = (byte) ((val >> 16) & 0xFF);
559                         code [code_len++] = (byte) ((val >> 24) & 0xFF);
560                         code [code_len++] = (byte) ((val >> 32) & 0xFF);
561                         code [code_len++] = (byte) ((val >> 40) & 0xFF);
562                         code [code_len++] = (byte) ((val >> 48) & 0xFF);
563                         code [code_len++] = (byte) ((val >> 56) & 0xFF);
564                 }
565                 
566                 public virtual void Emit (OpCode opcode, Label label)
567                 {
568                         int tlen = target_len (opcode);
569                         make_room (6);
570                         ll_emit (opcode);
571                         if (cur_stack > labels [label.label].maxStack)
572                                 labels [label.label].maxStack = cur_stack;
573                         
574                         if (fixups == null)
575                                 fixups = new LabelFixup [defaultFixupSize]; 
576                         else if (num_fixups >= fixups.Length) {
577                                 LabelFixup[] newf = new LabelFixup [fixups.Length + 16];
578                                 System.Array.Copy (fixups, newf, fixups.Length);
579                                 fixups = newf;
580                         }
581                         fixups [num_fixups].offset = tlen;
582                         fixups [num_fixups].pos = code_len;
583                         fixups [num_fixups].label_idx = label.label;
584                         num_fixups++;
585                         code_len += tlen;
586
587                 }
588                 
589                 public virtual void Emit (OpCode opcode, Label[] labels)
590                 {
591                         /* opcode needs to be switch. */
592                         int count = labels.Length;
593                         make_room (6 + count * 4);
594                         ll_emit (opcode);
595
596                         for (int i = 0; i < count; ++i)
597                                 if (cur_stack > this.labels [labels [i].label].maxStack)
598                                         this.labels [labels [i].label].maxStack = cur_stack;
599
600                         emit_int (count);
601                         if (fixups == null)
602                                 fixups = new LabelFixup [defaultFixupSize + count]; 
603                         else if (num_fixups + count >= fixups.Length) {
604                                 LabelFixup[] newf = new LabelFixup [fixups.Length + count + 16];
605                                 System.Array.Copy (fixups, newf, fixups.Length);
606                                 fixups = newf;
607                         }
608                         
609                         // ECMA 335, Partition III, p94 (7-10)
610                         //
611                         // The switch instruction implements a jump table. The format of 
612                         // the instruction is an unsigned int32 representing the number of targets N,
613                         // followed by N int32 values specifying jump targets: these targets are
614                         // represented as offsets (positive or negative) from the beginning of the 
615                         // instruction following this switch instruction.
616                         //
617                         // We must make sure it gets an offset from the *end* of the last label
618                         // (eg, the beginning of the instruction following this).
619                         //
620                         // remaining is the number of bytes from the current instruction to the
621                         // instruction that will be emitted.
622                         
623                         for (int i = 0, remaining = count * 4; i < count; ++i, remaining -= 4) {
624                                 fixups [num_fixups].offset = remaining;
625                                 fixups [num_fixups].pos = code_len;
626                                 fixups [num_fixups].label_idx = labels [i].label;
627                                 num_fixups++;
628                                 code_len += 4;
629                         }
630                 }
631
632                 public virtual void Emit (OpCode opcode, LocalBuilder lbuilder)
633                 {
634                         uint pos = lbuilder.position;
635                         bool load_addr = false;
636                         bool is_store = false;
637                         make_room (6);
638
639                         if (lbuilder.ilgen != this)
640                                 throw new Exception ("Trying to emit a local from a different ILGenerator.");
641
642                         /* inline the code from ll_emit () to optimize il code size */
643                         if (opcode.StackBehaviourPop == StackBehaviour.Pop1) {
644                                 cur_stack --;
645                                 is_store = true;
646                         } else {
647                                 cur_stack++;
648                                 if (cur_stack > max_stack)
649                                         max_stack = cur_stack;
650                                 load_addr = opcode.StackBehaviourPush == StackBehaviour.Pushi;
651                         }
652                         if (load_addr) {
653                                 if (pos < 256) {
654                                         code [code_len++] = (byte)0x12;
655                                         code [code_len++] = (byte)pos;
656                                 } else {
657                                         code [code_len++] = (byte)0xfe;
658                                         code [code_len++] = (byte)0x0d;
659                                         code [code_len++] = (byte)(pos & 0xff);
660                                         code [code_len++] = (byte)((pos >> 8) & 0xff);
661                                 }
662                         } else {
663                                 if (is_store) {
664                                         if (pos < 4) {
665                                                 code [code_len++] = (byte)(0x0a + pos);
666                                         } else if (pos < 256) {
667                                                 code [code_len++] = (byte)0x13;
668                                                 code [code_len++] = (byte)pos;
669                                         } else {
670                                                 code [code_len++] = (byte)0xfe;
671                                                 code [code_len++] = (byte)0x0e;
672                                                 code [code_len++] = (byte)(pos & 0xff);
673                                                 code [code_len++] = (byte)((pos >> 8) & 0xff);
674                                         }
675                                 } else {
676                                         if (pos < 4) {
677                                                 code [code_len++] = (byte)(0x06 + pos);
678                                         } else if (pos < 256) {
679                                                 code [code_len++] = (byte)0x11;
680                                                 code [code_len++] = (byte)pos;
681                                         } else {
682                                                 code [code_len++] = (byte)0xfe;
683                                                 code [code_len++] = (byte)0x0c;
684                                                 code [code_len++] = (byte)(pos & 0xff);
685                                                 code [code_len++] = (byte)((pos >> 8) & 0xff);
686                                         }
687                                 }
688                         }
689                 }
690
691                 public virtual void Emit (OpCode opcode, MethodInfo method)
692                 {
693                         if (method == null)
694                                 throw new ArgumentNullException ("method");
695
696                         int token = token_gen.GetToken (method);
697                         make_room (6);
698                         ll_emit (opcode);
699                         if (method.DeclaringType.Module == module)
700                                 add_token_fixup (method);
701                         emit_int (token);
702                         if (method.ReturnType != void_type)
703                                 cur_stack ++;
704
705                         if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
706                                 cur_stack -= method.GetParameterCount ();
707                 }
708
709                 private void Emit (OpCode opcode, MethodInfo method, int token)
710                 {
711                         make_room (6);
712                         ll_emit (opcode);
713                         if (method.DeclaringType.Module == module)
714                                 add_token_fixup (method);
715                         emit_int (token);
716                         if (method.ReturnType != void_type)
717                                 cur_stack ++;
718
719                         if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
720                                 cur_stack -= method.GetParameterCount ();
721                 }
722
723                 [CLSCompliant(false)]
724                 public void Emit (OpCode opcode, sbyte val)
725                 {
726                         make_room (3);
727                         ll_emit (opcode);
728                         code [code_len++] = (byte)val;
729                 }
730
731                 public virtual void Emit (OpCode opcode, SignatureHelper shelper)
732                 {
733                         int token = token_gen.GetToken (shelper);
734                         make_room (6);
735                         ll_emit (opcode);
736                         emit_int (token);
737                 }
738
739                 public virtual void Emit (OpCode opcode, float val)
740                 {
741                         byte[] s = System.BitConverter.GetBytes (val);
742                         make_room (6);
743                         ll_emit (opcode);
744                         if (BitConverter.IsLittleEndian){
745                                 System.Array.Copy (s, 0, code, code_len, 4);
746                                 code_len += 4;
747                         } else {
748                                 code [code_len++] = s [3];
749                                 code [code_len++] = s [2];
750                                 code [code_len++] = s [1];
751                                 code [code_len++] = s [0];                              
752                         }
753                 }
754
755                 public virtual void Emit (OpCode opcode, string val)
756                 {
757                         int token = token_gen.GetToken (val);
758                         make_room (6);
759                         ll_emit (opcode);
760                         emit_int (token);
761                 }
762
763                 public virtual void Emit (OpCode opcode, Type type)
764                 {
765                         make_room (6);
766                         ll_emit (opcode);
767                         emit_int (token_gen.GetToken (type));
768                 }
769
770                 [MonoTODO ("Do something about varargs method")]
771                 public void EmitCall (OpCode opcode, MethodInfo methodinfo, Type[] optionalParamTypes)
772                 {
773                         if (methodinfo == null)
774                                 throw new ArgumentNullException ("methodinfo can not be null");
775                         short value = opcode.Value;
776                         if (!(value == OpCodes.Call.Value || value == OpCodes.Callvirt.Value))
777                                 throw new NotSupportedException ("Only Call and CallVirt are allowed");
778                         if (optionalParamTypes != null){
779                                 if ((methodinfo.CallingConvention & CallingConventions.VarArgs)  == 0){
780                                         throw new InvalidOperationException ("Method is not VarArgs method and optional types were passed");
781                                 }
782
783                                 int token = token_gen.GetToken (methodinfo, optionalParamTypes);
784                                 Emit (opcode, methodinfo, token);
785                                 return;
786                         }
787                         Emit (opcode, methodinfo);
788                 }
789
790                 public void EmitCalli (OpCode opcode, CallingConvention unmanagedCallConv, Type returnType, Type[] paramTypes)
791                 {
792                         SignatureHelper helper 
793                                 = SignatureHelper.GetMethodSigHelper (module, 0, unmanagedCallConv, returnType, paramTypes);
794                         Emit (opcode, helper);
795                 }
796
797                 public void EmitCalli (OpCode opcode, CallingConventions callConv, Type returnType, Type[] paramTypes, Type[] optionalParamTypes)
798                 {
799                         if (optionalParamTypes != null)
800                                 throw new NotImplementedException ();
801
802                         SignatureHelper helper 
803                                 = SignatureHelper.GetMethodSigHelper (module, callConv, 0, returnType, paramTypes);
804                         Emit (opcode, helper);
805                 }
806                 
807                 public virtual void EmitWriteLine (FieldInfo field)
808                 {
809                         if (field == null)
810                                 throw new ArgumentNullException ("field");
811                         
812                         // The MS implementation does not check for valuetypes here but it
813                         // should. Also, it should check that if the field is not static,
814                         // then it is a member of this type.
815                         if (field.IsStatic)
816                                 Emit (OpCodes.Ldsfld, field);
817                         else {
818                                 Emit (OpCodes.Ldarg_0);
819                                 Emit (OpCodes.Ldfld, field);
820                         }
821                         Emit (OpCodes.Call, 
822                               typeof (Console).GetMethod ("WriteLine",
823                                                           new Type[1] { field.FieldType }));
824                 }
825
826                 public virtual void EmitWriteLine (LocalBuilder lbuilder)
827                 {
828                         if (lbuilder == null)
829                                 throw new ArgumentNullException ("lbuilder");
830                         if (lbuilder.LocalType is TypeBuilder)
831                                 throw new  ArgumentException ("Output streams do not support TypeBuilders.");
832                         // The MS implementation does not check for valuetypes here but it
833                         // should.
834                         Emit (OpCodes.Ldloc, lbuilder);
835                         Emit (OpCodes.Call, 
836                               typeof (Console).GetMethod ("WriteLine",
837                                                           new Type[1] { lbuilder.LocalType }));
838                 }
839                 
840                 public virtual void EmitWriteLine (string val)
841                 {
842                         Emit (OpCodes.Ldstr, val);
843                         Emit (OpCodes.Call, 
844                               typeof (Console).GetMethod ("WriteLine",
845                                                           new Type[1] { typeof(string)}));
846                 }
847
848                 public virtual void EndExceptionBlock ()
849                 {
850                         if (open_blocks.Count <= 0)
851                                 throw new NotSupportedException ("Not in an exception block");
852                         InternalEndClause ();
853                         MarkLabel (ex_handlers [cur_block].end);
854                         ex_handlers [cur_block].End (code_len);
855                         ex_handlers [cur_block].Debug (cur_block);
856                         //System.Console.WriteLine ("End Block {0} (handlers: {1})", cur_block, ex_handlers [cur_block].NumHandlers ());
857                         open_blocks.Pop ();
858                         if (open_blocks.Count > 0)
859                                 cur_block = (int)open_blocks.Peek ();
860                         //Console.WriteLine ("curblock restored to {0}", cur_block);
861                         //throw new NotImplementedException ();
862                 }
863
864                 public virtual void EndScope ()
865                 { }
866
867                 public virtual void MarkLabel (Label loc)
868                 {
869                         if (loc.label < 0 || loc.label >= num_labels)
870                                 throw new System.ArgumentException ("The label is not valid");
871                         if (labels [loc.label].addr >= 0)
872                                 throw new System.ArgumentException ("The label was already defined");
873                         labels [loc.label].addr = code_len;
874                         if (labels [loc.label].maxStack > cur_stack)
875                                 cur_stack = labels [loc.label].maxStack;
876                 }
877
878                 public virtual void MarkSequencePoint (ISymbolDocumentWriter document, int startLine,
879                                                        int startColumn, int endLine, int endColumn)
880                 {
881                         if (currentSequence == null || currentSequence.Document != document) {
882                                 if (sequencePointLists == null)
883                                         sequencePointLists = new ArrayList ();
884                                 currentSequence = new SequencePointList (document);
885                                 sequencePointLists.Add (currentSequence);
886                         }
887                         
888                         currentSequence.AddSequencePoint (code_len, startLine, startColumn, endLine, endColumn);
889                 }
890                 
891                 internal void GenerateDebugInfo (ISymbolWriter symbolWriter)
892                 {
893                         if (sequencePointLists != null) {
894                                 SequencePointList first = (SequencePointList) sequencePointLists [0];
895                                 SequencePointList last = (SequencePointList) sequencePointLists [sequencePointLists.Count - 1];
896                                 symbolWriter.SetMethodSourceRange (first.Document, first.StartLine, first.StartColumn, last.Document, last.EndLine, last.EndColumn);
897                                 
898                                 foreach (SequencePointList list in sequencePointLists)
899                                         symbolWriter.DefineSequencePoints (list.Document, list.GetOffsets(), list.GetLines(), list.GetColumns(), list.GetEndLines(), list.GetEndColumns());
900                                 
901                                 if (locals != null) {
902                                         foreach (LocalBuilder local in locals) {
903                                                 if (local.Name != null && local.Name.Length > 0) {
904                                                         SignatureHelper sighelper = SignatureHelper.GetLocalVarSigHelper (module);
905                                                         sighelper.AddArgument (local.LocalType);
906                                                         byte[] signature = sighelper.GetSignature ();
907                                                         symbolWriter.DefineLocalVariable (local.Name, FieldAttributes.Public, signature, SymAddressKind.ILOffset, local.position, 0, 0, local.StartOffset, local.EndOffset);
908                                                 }
909                                         }
910                                 }
911                                 sequencePointLists = null;
912                         }
913                 }
914                 
915                 internal bool HasDebugInfo
916                 {
917                         get { return sequencePointLists != null; }
918                 }
919
920                 public virtual void ThrowException (Type exceptionType)
921                 {
922                         if (exceptionType == null)
923                                 throw new ArgumentNullException ("exceptionType");
924                         if (! ((exceptionType == typeof (Exception)) || 
925                                    exceptionType.IsSubclassOf (typeof (Exception))))
926                                 throw new ArgumentException ("Type should be an exception type", "exceptionType");
927                         ConstructorInfo ctor = exceptionType.GetConstructor (new Type[0]);
928                         if (ctor == null)
929                                 throw new ArgumentException ("Type should have a default constructor", "exceptionType");
930                         Emit (OpCodes.Newobj, ctor);
931                         Emit (OpCodes.Throw);
932                 }
933
934                 [MonoTODO]
935                 public void UsingNamespace (String usingNamespace)
936                 {
937                         throw new NotImplementedException ();
938                 }
939
940                 internal void label_fixup ()
941                 {
942                         for (int i = 0; i < num_fixups; ++i) {
943                                 
944                                 // Diff is the offset from the end of the jump instruction to the address of the label
945                                 int diff = labels [fixups [i].label_idx].addr - (fixups [i].pos + fixups [i].offset);
946                                 if (fixups [i].offset == 1) {
947                                         code [fixups [i].pos] = (byte)((sbyte) diff);
948                                 } else {
949                                         int old_cl = code_len;
950                                         code_len = fixups [i].pos;
951                                         emit_int (diff);
952                                         code_len = old_cl;
953                                 }
954                         }
955                 }
956
957                 internal static int Mono_GetCurrentOffset (ILGenerator ig)
958                 {
959                         return ig.code_len;
960                 }
961
962                 void _ILGenerator.GetIDsOfNames ([In] ref Guid riid, IntPtr rgszNames, uint cNames, uint lcid, IntPtr rgDispId)
963                 {
964                         throw new NotImplementedException ();
965                 }
966
967                 void _ILGenerator.GetTypeInfo (uint iTInfo, uint lcid, IntPtr ppTInfo)
968                 {
969                         throw new NotImplementedException ();
970                 }
971
972                 void _ILGenerator.GetTypeInfoCount (out uint pcTInfo)
973                 {
974                         throw new NotImplementedException ();
975                 }
976
977                 void _ILGenerator.Invoke (uint dispIdMember, [In] ref Guid riid, uint lcid, short wFlags, IntPtr pDispParams, IntPtr pVarResult, IntPtr pExcepInfo, IntPtr puArgErr)
978                 {
979                         throw new NotImplementedException ();
980                 }
981         }
982         
983         internal class SequencePointList
984         {
985                 ISymbolDocumentWriter doc;
986                 SequencePoint[] points;
987                 int count;
988                 const int arrayGrow = 10;
989                 
990                 public SequencePointList (ISymbolDocumentWriter doc)
991                 {
992                         this.doc = doc;
993                 }
994                 
995                 public ISymbolDocumentWriter Document {
996                         get { return doc; }
997                 }
998                 
999                 public int[] GetOffsets()
1000                 {
1001                         int[] data = new int [count];
1002                         for (int n=0; n<count; n++) data [n] = points[n].Offset;
1003                         return data; 
1004                 }
1005                 public int[] GetLines()
1006                 {
1007                         int[] data = new int [count];
1008                         for (int n=0; n<count; n++) data [n] = points[n].Line;
1009                         return data; 
1010                 }
1011                 public int[] GetColumns()
1012                 {
1013                         int[] data = new int [count];
1014                         for (int n=0; n<count; n++) data [n] = points[n].Col;
1015                         return data; 
1016                 }
1017                 public int[] GetEndLines()
1018                 {
1019                         int[] data = new int [count];
1020                         for (int n=0; n<count; n++) data [n] = points[n].EndLine;
1021                         return data; 
1022                 }
1023                 public int[] GetEndColumns()
1024                 {
1025                         int[] data = new int [count];
1026                         for (int n=0; n<count; n++) data [n] = points[n].EndCol;
1027                         return data; 
1028                 }
1029                 public int StartLine {
1030                         get { return points[0].Line; }
1031                 }
1032                 public int EndLine {
1033                         get { return points[count - 1].Line; }
1034                 }
1035                 public int StartColumn {
1036                         get { return points[0].Col; }
1037                 }
1038                 public int EndColumn {
1039                         get { return points[count - 1].Col; }
1040                 }
1041                 
1042                 public void AddSequencePoint (int offset, int line, int col, int endLine, int endCol)
1043                 {
1044                         SequencePoint s = new SequencePoint ();
1045                         s.Offset = offset;
1046                         s.Line = line;
1047                         s.Col = col;
1048                         s.EndLine = endLine;
1049                         s.EndCol = endCol;
1050                         
1051                         if (points == null) {
1052                                 points = new SequencePoint [arrayGrow];
1053                         } else if (count >= points.Length) {
1054                                 SequencePoint[] temp = new SequencePoint [count + arrayGrow];
1055                                 Array.Copy (points, temp, points.Length);
1056                                 points = temp;
1057                         }
1058                         
1059                         points [count] = s;
1060                         count++;
1061                 }
1062         }
1063         
1064         struct SequencePoint {
1065                 public int Offset;
1066                 public int Line;
1067                 public int Col;
1068                 public int EndLine;
1069                 public int EndCol;
1070         }
1071 }