2004-07-22 Martin Baulig <martin@ximian.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 using Mono.CSharp.Debugger;
39
40 namespace System.Reflection.Emit {
41
42         internal struct ILExceptionBlock {
43                 public const int CATCH = 0;
44                 public const int FILTER = 1;
45                 public const int FINALLY = 2;
46                 public const int FAULT = 4;
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 ("");
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 End (int offset)
109                 {
110                         if (handlers == null)
111                                 return;
112                         int i = handlers.Length - 1;
113                         if (i >= 0)
114                                 handlers [i].len = offset - handlers [i].start;
115                 }
116
117                 internal int LastClauseType ()
118                 {
119                         if (handlers != null)
120                                 return handlers [handlers.Length-1].type;
121                         else
122                                 return ILExceptionBlock.CATCH;
123                 }
124
125                 internal void Debug (int b)
126                 {
127 #if NO
128                         System.Console.WriteLine ("Handler {0} at {1}, len: {2}", b, start, len);
129                         for (int i = 0; i < handlers.Length; ++i)
130                                 handlers [i].Debug ();
131 #endif
132                 }
133
134                 void add_block (int offset)
135                 {
136                         if (handlers != null) {
137                                 int i = handlers.Length;
138                                 ILExceptionBlock[] new_b = new ILExceptionBlock [i + 1];
139                                 System.Array.Copy (handlers, new_b, i);
140                                 handlers = new_b;
141                                 handlers [i].len = offset - handlers [i].start;
142                         } else {
143                                 handlers = new ILExceptionBlock [1];
144                                 len = offset - start;
145                         }
146                 }
147         }
148         
149         internal struct ILTokenInfo {
150                 public MemberInfo member;
151                 public int code_pos;
152         }
153
154         internal interface TokenGenerator {
155                 int GetToken (string str);
156
157                 int GetToken (MemberInfo member);
158
159                 int GetToken (MethodInfo method, Type[] opt_param_types);
160
161                 int GetToken (SignatureHelper helper);
162         }               
163
164         public class ILGenerator: Object {
165                 private struct LabelFixup {
166                         public int offset;    // The number of bytes between pos and the
167                                                               // offset of the jump
168                         public int pos;       // Where offset of the label is placed
169                         public int label_idx; // The label to jump to
170                 };
171                 
172                 struct LabelData {
173                         public LabelData (int addr, int maxStack)
174                         {
175                                 this.addr = addr;
176                                 this.maxStack = maxStack;
177                         }
178                         
179                         public int addr;
180                         public int maxStack; 
181                 }
182                 
183                 static readonly Type void_type = typeof (void);
184                 #region Sync with reflection.h
185                 private byte[] code;
186                 private int code_len;
187                 private int max_stack;
188                 private int cur_stack;
189                 private LocalBuilder[] locals;
190                 private ILExceptionInfo[] ex_handlers;
191                 private int num_token_fixups;
192                 private ILTokenInfo[] token_fixups;
193                 #endregion
194                 
195                 private LabelData [] labels;
196                 private int num_labels;
197                 private LabelFixup[] fixups;
198                 private int num_fixups;
199                 internal Module module;
200                 internal IMonoSymbolWriter sym_writer;
201                 private Stack scopes;
202                 private int cur_block;
203                 private Stack open_blocks;
204                 private TokenGenerator token_gen;
205                 
206                 const int defaultFixupSize = 8;
207                 const int defaultLabelsSize = 8;
208
209                 internal ILGenerator (Module m, TokenGenerator token_gen, int size)
210                 {
211                         if (size < 0)
212                                 size = 128;
213                         code_len = 0;
214                         code = new byte [size];
215                         cur_stack = max_stack = 0;
216                         num_fixups = num_labels = 0;
217                         token_fixups = new ILTokenInfo [8];
218                         num_token_fixups = 0;
219                         module = m;
220                         if (module is ModuleBuilder)
221                                 sym_writer = ((ModuleBuilder)module).symbol_writer;
222                         open_blocks = new Stack ();
223                         this.token_gen = token_gen;
224                 }
225
226                 private void add_token_fixup (MemberInfo mi)
227                 {
228                         if (num_token_fixups == token_fixups.Length) {
229                                 ILTokenInfo[] ntf = new ILTokenInfo [num_token_fixups * 2];
230                                 token_fixups.CopyTo (ntf, 0);
231                                 token_fixups = ntf;
232                         }
233                         token_fixups [num_token_fixups].member = mi;
234                         token_fixups [num_token_fixups++].code_pos = code_len;
235                 }
236
237                 private void make_room (int nbytes)
238                 {
239                         if (code_len + nbytes < code.Length)
240                                 return;
241                         byte[] new_code = new byte [(code_len + nbytes) * 2 + 128];
242                         System.Array.Copy (code, 0, new_code, 0, code.Length);
243                         code = new_code;
244                 }
245
246                 private void emit_int (int val)
247                 {
248                         code [code_len++] = (byte) (val & 0xFF);
249                         code [code_len++] = (byte) ((val >> 8) & 0xFF);
250                         code [code_len++] = (byte) ((val >> 16) & 0xFF);
251                         code [code_len++] = (byte) ((val >> 24) & 0xFF);
252                 }
253
254                 /* change to pass by ref to avoid copy */
255                 private void ll_emit (OpCode opcode)
256                 {
257                         /* 
258                          * there is already enough room allocated in code.
259                          */
260                         // access op1 and op2 directly since the Value property is useless
261                         if (opcode.Size == 2)
262                                 code [code_len++] = opcode.op1;
263                         code [code_len++] = opcode.op2;
264                         /*
265                          * We should probably keep track of stack needs here.
266                          * Or we may want to run the verifier on the code before saving it
267                          * (this may be needed anyway when the ILGenerator is not used...).
268                          */
269                         switch (opcode.StackBehaviourPush) {
270                         case StackBehaviour.Push1:
271                         case StackBehaviour.Pushi:
272                         case StackBehaviour.Pushi8:
273                         case StackBehaviour.Pushr4:
274                         case StackBehaviour.Pushr8:
275                         case StackBehaviour.Pushref:
276                         case StackBehaviour.Varpush: /* again we are conservative and assume it pushes 1 */
277                                 cur_stack ++;
278                                 break;
279                         case StackBehaviour.Push1_push1:
280                                 cur_stack += 2;
281                                 break;
282                         }
283                         if (max_stack < cur_stack)
284                                 max_stack = cur_stack;
285
286                         /* 
287                          * Note that we adjust for the pop behaviour _after_ setting max_stack.
288                          */
289                         switch (opcode.StackBehaviourPop) {
290                         case StackBehaviour.Varpop:
291                                 break; /* we are conservative and assume it doesn't decrease the stack needs */
292                         case StackBehaviour.Pop1:
293                         case StackBehaviour.Popi:
294                         case StackBehaviour.Popref:
295                                 cur_stack --;
296                                 break;
297                         case StackBehaviour.Pop1_pop1:
298                         case StackBehaviour.Popi_pop1:
299                         case StackBehaviour.Popi_popi:
300                         case StackBehaviour.Popi_popi8:
301                         case StackBehaviour.Popi_popr4:
302                         case StackBehaviour.Popi_popr8:
303                         case StackBehaviour.Popref_pop1:
304                         case StackBehaviour.Popref_popi:
305                                 cur_stack -= 2;
306                                 break;
307                         case StackBehaviour.Popi_popi_popi:
308                         case StackBehaviour.Popref_popi_popi:
309                         case StackBehaviour.Popref_popi_popi8:
310                         case StackBehaviour.Popref_popi_popr4:
311                         case StackBehaviour.Popref_popi_popr8:
312                         case StackBehaviour.Popref_popi_popref:
313                                 cur_stack -= 3;
314                                 break;
315                         }
316                 }
317
318                 private static int target_len (OpCode opcode)
319                 {
320                         if (opcode.OperandType == OperandType.InlineBrTarget)
321                                 return 4;
322                         return 1;
323                 }
324
325                 private void InternalEndClause ()
326                 {
327                         switch (ex_handlers [cur_block].LastClauseType ()) {
328                         case ILExceptionBlock.CATCH:
329                                 // how could we optimize code size here?
330                                 Emit (OpCodes.Leave, ex_handlers [cur_block].end);
331                                 break;
332                         case ILExceptionBlock.FAULT:
333                         case ILExceptionBlock.FINALLY:
334                                 Emit (OpCodes.Endfinally);
335                                 break;
336                         case ILExceptionBlock.FILTER:
337                                 Emit (OpCodes.Endfilter);
338                                 break;
339                         }
340                 }
341
342                 public virtual void BeginCatchBlock (Type exceptionType)
343                 {
344                         if (open_blocks.Count <= 0)
345                                 throw new NotSupportedException ("Not in an exception block");
346                         InternalEndClause ();
347                         ex_handlers [cur_block].AddCatch (exceptionType, code_len);
348                         cur_stack = 1; // the exception object is on the stack by default
349                         if (max_stack < cur_stack)
350                                 max_stack = cur_stack;
351                         //System.Console.WriteLine ("Begin catch Block: {0} {1}",exceptionType.ToString(), max_stack);
352                         //throw new NotImplementedException ();
353                 }
354
355                 [MonoTODO]
356                 public virtual void BeginExceptFilterBlock ()
357                 {
358                         throw new NotImplementedException ();
359                 }
360
361                 public virtual Label BeginExceptionBlock ()
362                 {
363                         //System.Console.WriteLine ("Begin Block");
364                         
365                         if (ex_handlers != null) {
366                                 cur_block = ex_handlers.Length;
367                                 ILExceptionInfo[] new_ex = new ILExceptionInfo [cur_block + 1];
368                                 System.Array.Copy (ex_handlers, new_ex, cur_block);
369                                 ex_handlers = new_ex;
370                         } else {
371                                 ex_handlers = new ILExceptionInfo [1];
372                                 cur_block = 0;
373                         }
374                         open_blocks.Push (cur_block);
375                         ex_handlers [cur_block].start = code_len;
376                         return ex_handlers [cur_block].end = DefineLabel ();
377                 }
378
379                 public virtual void BeginFaultBlock()
380                 {
381                         if (open_blocks.Count <= 0)
382                                 throw new NotSupportedException ("Not in an exception block");
383                         //System.Console.WriteLine ("Begin fault Block");
384                         ex_handlers [cur_block].AddFault (code_len);
385                 }
386                 
387                 public virtual void BeginFinallyBlock()
388                 {
389                         if (open_blocks.Count <= 0)
390                                 throw new NotSupportedException ("Not in an exception block");
391                         InternalEndClause ();
392                         //System.Console.WriteLine ("Begin finally Block");
393                         ex_handlers [cur_block].AddFinally (code_len);
394                 }
395                 
396                 public virtual void BeginScope ()
397                 {
398                         if (sym_writer != null) {
399                                 if (scopes == null)
400                                         scopes = new Stack ();
401                                 scopes.Push (sym_writer.OpenScope (code_len));
402                         }
403                 }
404                 
405                 public LocalBuilder DeclareLocal (Type localType)
406                 {
407                         return DeclareLocal (localType, false);
408                 }
409
410
411 #if NET_2_0
412                 public
413 #else
414                 internal
415 #endif
416                 LocalBuilder DeclareLocal (Type localType, bool pinned)
417                 {
418                         LocalBuilder res = new LocalBuilder (localType, this);
419                         res.is_pinned = pinned;
420                         
421                         if (locals != null) {
422                                 LocalBuilder[] new_l = new LocalBuilder [locals.Length + 1];
423                                 System.Array.Copy (locals, new_l, locals.Length);
424                                 new_l [locals.Length] = res;
425                                 locals = new_l;
426                         } else {
427                                 locals = new LocalBuilder [1];
428                                 locals [0] = res;
429                         }
430                         res.position = (ushort)(locals.Length - 1);
431                         return res;
432                 }
433                 
434                 public virtual Label DefineLabel ()
435                 {
436                         if (labels == null)
437                                 labels = new LabelData [defaultLabelsSize];
438                         else if (num_labels >= labels.Length) {
439                                 LabelData [] t = new LabelData [labels.Length * 2];
440                                 Array.Copy (labels, t, labels.Length);
441                                 labels = t;
442                         }
443                         
444                         labels [num_labels] = new LabelData (-1, 0);
445                         
446                         return new Label (num_labels++);
447                 }
448                 
449                 public virtual void Emit (OpCode opcode)
450                 {
451                         make_room (2);
452                         ll_emit (opcode);
453                 }
454                 
455                 public virtual void Emit (OpCode opcode, Byte val)
456                 {
457                         make_room (3);
458                         ll_emit (opcode);
459                         code [code_len++] = val;
460                 }
461                 
462                 public virtual void Emit (OpCode opcode, ConstructorInfo constructor)
463                 {
464                         int token = token_gen.GetToken (constructor);
465                         make_room (6);
466                         ll_emit (opcode);
467                         if (constructor.DeclaringType.Module == module)
468                                 add_token_fixup (constructor);
469                         emit_int (token);
470                         
471                         if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
472                                 cur_stack -= constructor.GetParameterCount ();
473                 }
474                 
475                 public virtual void Emit (OpCode opcode, double val)
476                 {
477                         Double.AssertEndianity (out val);
478
479                         byte[] s = System.BitConverter.GetBytes (val);
480                         make_room (10);
481                         ll_emit (opcode);
482                         if (BitConverter.IsLittleEndian){
483                                 System.Array.Copy (s, 0, code, code_len, 8);
484                                 code_len += 8;
485                         } else {
486                                 code [code_len++] = s [7];
487                                 code [code_len++] = s [6];
488                                 code [code_len++] = s [5];
489                                 code [code_len++] = s [4];
490                                 code [code_len++] = s [3];
491                                 code [code_len++] = s [2];
492                                 code [code_len++] = s [1];
493                                 code [code_len++] = s [0];                              
494                         }
495                 }
496                 
497                 public virtual void Emit (OpCode opcode, FieldInfo field)
498                 {
499                         int token = token_gen.GetToken (field);
500                         make_room (6);
501                         ll_emit (opcode);
502                         if (field.DeclaringType.Module == module)
503                                 add_token_fixup (field);
504                         emit_int (token);
505                 }
506                 
507                 public virtual void Emit (OpCode opcode, Int16 val)
508                 {
509                         make_room (4);
510                         ll_emit (opcode);
511                         code [code_len++] = (byte) (val & 0xFF);
512                         code [code_len++] = (byte) ((val >> 8) & 0xFF);
513                 }
514                 
515                 public virtual void Emit (OpCode opcode, int val)
516                 {
517                         make_room (6);
518                         ll_emit (opcode);
519                         emit_int (val);
520                 }
521                 
522                 public virtual void Emit (OpCode opcode, long val)
523                 {
524                         make_room (10);
525                         ll_emit (opcode);
526                         code [code_len++] = (byte) (val & 0xFF);
527                         code [code_len++] = (byte) ((val >> 8) & 0xFF);
528                         code [code_len++] = (byte) ((val >> 16) & 0xFF);
529                         code [code_len++] = (byte) ((val >> 24) & 0xFF);
530                         code [code_len++] = (byte) ((val >> 32) & 0xFF);
531                         code [code_len++] = (byte) ((val >> 40) & 0xFF);
532                         code [code_len++] = (byte) ((val >> 48) & 0xFF);
533                         code [code_len++] = (byte) ((val >> 56) & 0xFF);
534                 }
535                 
536                 public virtual void Emit (OpCode opcode, Label label)
537                 {
538                         int tlen = target_len (opcode);
539                         make_room (6);
540                         ll_emit (opcode);
541                         if (cur_stack > labels [label.label].maxStack)
542                                 labels [label.label].maxStack = cur_stack;
543                         
544                         if (fixups == null)
545                                 fixups = new LabelFixup [defaultFixupSize]; 
546                         else if (num_fixups >= fixups.Length) {
547                                 LabelFixup[] newf = new LabelFixup [fixups.Length + 16];
548                                 System.Array.Copy (fixups, newf, fixups.Length);
549                                 fixups = newf;
550                         }
551                         fixups [num_fixups].offset = tlen;
552                         fixups [num_fixups].pos = code_len;
553                         fixups [num_fixups].label_idx = label.label;
554                         num_fixups++;
555                         code_len += tlen;
556
557                 }
558                 
559                 public virtual void Emit (OpCode opcode, Label[] labels)
560                 {
561                         /* opcode needs to be switch. */
562                         int count = labels.Length;
563                         make_room (6 + count * 4);
564                         ll_emit (opcode);
565
566                         for (int i = 0; i < count; ++i)
567                                 if (cur_stack > this.labels [labels [i].label].maxStack)
568                                         this.labels [labels [i].label].maxStack = cur_stack;
569
570                         emit_int (count);
571                         if (fixups == null)
572                                 fixups = new LabelFixup [defaultFixupSize + count]; 
573                         else if (num_fixups + count >= fixups.Length) {
574                                 LabelFixup[] newf = new LabelFixup [fixups.Length + count + 16];
575                                 System.Array.Copy (fixups, newf, fixups.Length);
576                                 fixups = newf;
577                         }
578                         
579                         // ECMA 335, Partition III, p94 (7-10)
580                         //
581                         // The switch instruction implements a jump table. The format of 
582                         // the instruction is an unsigned int32 representing the number of targets N,
583                         // followed by N int32 values specifying jump targets: these targets are
584                         // represented as offsets (positive or negative) from the beginning of the 
585                         // instruction following this switch instruction.
586                         //
587                         // We must make sure it gets an offset from the *end* of the last label
588                         // (eg, the beginning of the instruction following this).
589                         //
590                         // remaining is the number of bytes from the current instruction to the
591                         // instruction that will be emitted.
592                         
593                         for (int i = 0, remaining = count * 4; i < count; ++i, remaining -= 4) {
594                                 fixups [num_fixups].offset = remaining;
595                                 fixups [num_fixups].pos = code_len;
596                                 fixups [num_fixups].label_idx = labels [i].label;
597                                 num_fixups++;
598                                 code_len += 4;
599                         }
600                 }
601
602                 public virtual void Emit (OpCode opcode, LocalBuilder lbuilder)
603                 {
604                         uint pos = lbuilder.position;
605                         bool load_addr = false;
606                         bool is_store = false;
607                         make_room (6);
608
609                         if (lbuilder.ilgen != this)
610                                 throw new Exception ("Trying to emit a local from a different ILGenerator.");
611
612                         /* inline the code from ll_emit () to optimize il code size */
613                         if (opcode.StackBehaviourPop == StackBehaviour.Pop1) {
614                                 cur_stack --;
615                                 is_store = true;
616                         } else {
617                                 cur_stack++;
618                                 if (cur_stack > max_stack)
619                                         max_stack = cur_stack;
620                                 load_addr = opcode.StackBehaviourPush == StackBehaviour.Pushi;
621                         }
622                         if (load_addr) {
623                                 if (pos < 256) {
624                                         code [code_len++] = (byte)0x12;
625                                         code [code_len++] = (byte)pos;
626                                 } else {
627                                         code [code_len++] = (byte)0xfe;
628                                         code [code_len++] = (byte)0x0d;
629                                         code [code_len++] = (byte)(pos & 0xff);
630                                         code [code_len++] = (byte)((pos >> 8) & 0xff);
631                                 }
632                         } else {
633                                 if (is_store) {
634                                         if (pos < 4) {
635                                                 code [code_len++] = (byte)(0x0a + pos);
636                                         } else if (pos < 256) {
637                                                 code [code_len++] = (byte)0x13;
638                                                 code [code_len++] = (byte)pos;
639                                         } else {
640                                                 code [code_len++] = (byte)0xfe;
641                                                 code [code_len++] = (byte)0x0e;
642                                                 code [code_len++] = (byte)(pos & 0xff);
643                                                 code [code_len++] = (byte)((pos >> 8) & 0xff);
644                                         }
645                                 } else {
646                                         if (pos < 4) {
647                                                 code [code_len++] = (byte)(0x06 + pos);
648                                         } else if (pos < 256) {
649                                                 code [code_len++] = (byte)0x11;
650                                                 code [code_len++] = (byte)pos;
651                                         } else {
652                                                 code [code_len++] = (byte)0xfe;
653                                                 code [code_len++] = (byte)0x0c;
654                                                 code [code_len++] = (byte)(pos & 0xff);
655                                                 code [code_len++] = (byte)((pos >> 8) & 0xff);
656                                         }
657                                 }
658                         }
659                 }
660
661                 public virtual void Emit (OpCode opcode, MethodInfo method)
662                 {
663                         if (method == null)
664                                 throw new ArgumentNullException ("method");
665
666                         int token = token_gen.GetToken (method);
667                         make_room (6);
668                         ll_emit (opcode);
669                         if (method.DeclaringType.Module == module)
670                                 add_token_fixup (method);
671                         emit_int (token);
672                         if (method.ReturnType != void_type)
673                                 cur_stack ++;
674
675                         if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
676                                 cur_stack -= method.GetParameterCount ();
677                 }
678
679                 private void Emit (OpCode opcode, MethodInfo method, int token)
680                 {
681                         make_room (6);
682                         ll_emit (opcode);
683                         if (method.DeclaringType.Module == module)
684                                 add_token_fixup (method);
685                         emit_int (token);
686                         if (method.ReturnType != void_type)
687                                 cur_stack ++;
688
689                         if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
690                                 cur_stack -= method.GetParameterCount ();
691                 }
692
693                 [CLSCompliant(false)]
694                 public void Emit (OpCode opcode, sbyte val)
695                 {
696                         make_room (3);
697                         ll_emit (opcode);
698                         code [code_len++] = (byte)val;
699                 }
700
701                 public virtual void Emit (OpCode opcode, SignatureHelper shelper)
702                 {
703                         int token = token_gen.GetToken (shelper);
704                         make_room (6);
705                         ll_emit (opcode);
706                         emit_int (token);
707                 }
708
709                 public virtual void Emit (OpCode opcode, float val)
710                 {
711                         byte[] s = System.BitConverter.GetBytes (val);
712                         make_room (6);
713                         ll_emit (opcode);
714                         if (BitConverter.IsLittleEndian){
715                                 System.Array.Copy (s, 0, code, code_len, 4);
716                                 code_len += 4;
717                         } else {
718                                 code [code_len++] = s [3];
719                                 code [code_len++] = s [2];
720                                 code [code_len++] = s [1];
721                                 code [code_len++] = s [0];                              
722                         }
723                 }
724
725                 public virtual void Emit (OpCode opcode, string val)
726                 {
727                         int token = token_gen.GetToken (val);
728                         make_room (6);
729                         ll_emit (opcode);
730                         emit_int (token);
731                 }
732
733                 public virtual void Emit (OpCode opcode, Type type)
734                 {
735                         make_room (6);
736                         ll_emit (opcode);
737                         emit_int (token_gen.GetToken (type));
738                 }
739
740                 [MonoTODO ("Do something about varargs method")]
741                 public void EmitCall (OpCode opcode, MethodInfo methodinfo, Type[] optionalParamTypes)
742                 {
743                         if (methodinfo == null)
744                                 throw new ArgumentNullException ("methodinfo can not be null");
745                         short value = opcode.Value;
746                         if (!(value == OpCodes.Call.Value || value == OpCodes.Callvirt.Value))
747                                 throw new NotSupportedException ("Only Call and CallVirt are allowed");
748                         if (optionalParamTypes != null){
749                                 if ((methodinfo.CallingConvention & CallingConventions.VarArgs)  == 0){
750                                         throw new InvalidOperationException ("Method is not VarArgs method and optional types were passed");
751                                 }
752
753                                 int token = token_gen.GetToken (methodinfo, optionalParamTypes);
754                                 Emit (opcode, methodinfo, token);
755                                 return;
756                         }
757                         Emit (opcode, methodinfo);
758                 }
759
760                 public void EmitCalli (OpCode opcode, CallingConvention unmanagedCallConv, Type returnType, Type[] paramTypes)
761                 {
762                         SignatureHelper helper 
763                                 = SignatureHelper.GetMethodSigHelper (module, 0, unmanagedCallConv, returnType, paramTypes);
764                         Emit (opcode, helper);
765                 }
766
767                 public void EmitCalli (OpCode opcode, CallingConventions callConv, Type returnType, Type[] paramTypes, Type[] optionalParamTypes)
768                 {
769                         if (optionalParamTypes != null)
770                                 throw new NotImplementedException ();
771
772                         SignatureHelper helper 
773                                 = SignatureHelper.GetMethodSigHelper (module, callConv, 0, returnType, paramTypes);
774                         Emit (opcode, helper);
775                 }
776                 
777                 public virtual void EmitWriteLine (FieldInfo field)
778                 {
779                         if (field == null)
780                                 throw new ArgumentNullException ("field");
781                         
782                         // The MS implementation does not check for valuetypes here but it
783                         // should. Also, it should check that if the field is not static,
784                         // then it is a member of this type.
785                         if (field.IsStatic)
786                                 Emit (OpCodes.Ldsfld, field);
787                         else {
788                                 Emit (OpCodes.Ldarg_0);
789                                 Emit (OpCodes.Ldfld, field);
790                         }
791                         Emit (OpCodes.Call, 
792                               typeof (Console).GetMethod ("WriteLine",
793                                                           new Type[1] { field.FieldType }));
794                 }
795
796                 public virtual void EmitWriteLine (LocalBuilder lbuilder)
797                 {
798                         if (lbuilder == null)
799                                 throw new ArgumentNullException ("lbuilder");
800                         if (lbuilder.LocalType is TypeBuilder)
801                                 throw new  ArgumentException ("Output streams do not support TypeBuilders.");
802                         // The MS implementation does not check for valuetypes here but it
803                         // should.
804                         Emit (OpCodes.Ldloc, lbuilder);
805                         Emit (OpCodes.Call, 
806                               typeof (Console).GetMethod ("WriteLine",
807                                                           new Type[1] { lbuilder.LocalType }));
808                 }
809                 
810                 public virtual void EmitWriteLine (string val)
811                 {
812                         Emit (OpCodes.Ldstr, val);
813                         Emit (OpCodes.Call, 
814                               typeof (Console).GetMethod ("WriteLine",
815                                                           new Type[1] { typeof(string)}));
816                 }
817
818                 public virtual void EndExceptionBlock ()
819                 {
820                         if (open_blocks.Count <= 0)
821                                 throw new NotSupportedException ("Not in an exception block");
822                         InternalEndClause ();
823                         MarkLabel (ex_handlers [cur_block].end);
824                         ex_handlers [cur_block].End (code_len);
825                         ex_handlers [cur_block].Debug (cur_block);
826                         //System.Console.WriteLine ("End Block {0} (handlers: {1})", cur_block, ex_handlers [cur_block].NumHandlers ());
827                         open_blocks.Pop ();
828                         if (open_blocks.Count > 0)
829                                 cur_block = (int)open_blocks.Peek ();
830                         //Console.WriteLine ("curblock restored to {0}", cur_block);
831                         //throw new NotImplementedException ();
832                 }
833
834                 public virtual void EndScope ()
835                 {
836                         if (sym_writer != null) {
837                                 sym_writer.CloseScope (code_len);
838                                 if (scopes == null)
839                                         throw new InvalidOperationException ();
840                                 scopes.Pop ();
841                         }
842                 }
843
844                 public virtual void MarkLabel (Label loc)
845                 {
846                         if (loc.label < 0 || loc.label >= num_labels)
847                                 throw new System.ArgumentException ("The label is not valid");
848                         if (labels [loc.label].addr >= 0)
849                                 throw new System.ArgumentException ("The label was already defined");
850                         labels [loc.label].addr = code_len;
851                         if (labels [loc.label].maxStack > cur_stack)
852                                 cur_stack = labels [loc.label].maxStack;
853                 }
854
855                 public virtual void MarkSequencePoint (ISymbolDocumentWriter document, int startLine,
856                                                        int startColumn, int endLine, int endColumn)
857                 {
858                         if (sym_writer == null)
859                                 return;
860
861                         sym_writer.MarkSequencePoint (code_len, startLine, startColumn);
862                 }
863
864                 public virtual void ThrowException (Type exceptionType)
865                 {
866                         if (exceptionType == null)
867                                 throw new ArgumentNullException ("exceptionType");
868                         if (! ((exceptionType == typeof (Exception)) || 
869                                    exceptionType.IsSubclassOf (typeof (Exception))))
870                                 throw new ArgumentException ("Type should be an exception type", "exceptionType");
871                         ConstructorInfo ctor = exceptionType.GetConstructor (new Type[0]);
872                         if (ctor == null)
873                                 throw new ArgumentException ("Type should have a default constructor", "exceptionType");
874                         Emit (OpCodes.Newobj, ctor);
875                         Emit (OpCodes.Throw);
876                 }
877
878                 [MonoTODO]
879                 public void UsingNamespace (String usingNamespace)
880                 {
881                         throw new NotImplementedException ();
882                 }
883
884                 internal void label_fixup ()
885                 {
886                         for (int i = 0; i < num_fixups; ++i) {
887                                 
888                                 // Diff is the offset from the end of the jump instruction to the address of the label
889                                 int diff = labels [fixups [i].label_idx].addr - (fixups [i].pos + fixups [i].offset);
890                                 if (fixups [i].offset == 1) {
891                                         code [fixups [i].pos] = (byte)((sbyte) diff);
892                                 } else {
893                                         int old_cl = code_len;
894                                         code_len = fixups [i].pos;
895                                         emit_int (diff);
896                                         code_len = old_cl;
897                                 }
898                         }
899                 }
900         }
901 }