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