3 // System.Reflection.Emit/ILGenerator.cs
6 // Paolo Molaro (lupus@ximian.com)
8 // (C) 2001 Ximian, Inc. http://www.ximian.com
12 using System.Collections;
13 using System.Diagnostics.SymbolStore;
14 using System.Runtime.InteropServices;
15 using Mono.CSharp.Debugger;
17 namespace System.Reflection.Emit {
19 internal struct ILExceptionBlock {
20 public const int CATCH = 0;
21 public const int FILTER = 1;
22 public const int FINALLY = 2;
23 public const int FAULT = 4;
29 internal int filter_offset;
31 internal void Debug () {
33 System.Console.Write ("\ttype="+type.ToString()+" start="+start.ToString()+" len="+len.ToString());
35 System.Console.WriteLine (" extype="+extype.ToString());
37 System.Console.WriteLine ("");
41 internal struct ILExceptionInfo {
42 ILExceptionBlock[] handlers;
47 internal int NumHandlers ()
49 return handlers.Length;
52 internal void AddCatch (Type extype, int offset)
57 i = handlers.Length - 1;
58 handlers [i].type = ILExceptionBlock.CATCH;
59 handlers [i].start = offset;
60 handlers [i].extype = extype;
63 internal void AddFinally (int offset)
68 i = handlers.Length - 1;
69 handlers [i].type = ILExceptionBlock.FINALLY;
70 handlers [i].start = offset;
71 handlers [i].extype = null;
74 internal void End (int offset)
78 int i = handlers.Length - 1;
80 handlers [i].len = offset - handlers [i].start;
83 internal int LastClauseType ()
86 return handlers [handlers.Length-1].type;
88 return ILExceptionBlock.CATCH;
91 internal void Debug (int b)
94 System.Console.WriteLine ("Handler {0} at {1}, len: {2}", b, start, len);
95 for (int i = 0; i < handlers.Length; ++i)
96 handlers [i].Debug ();
100 void add_block (int offset)
102 if (handlers != null) {
103 int i = handlers.Length;
104 ILExceptionBlock[] new_b = new ILExceptionBlock [i + 1];
105 System.Array.Copy (handlers, new_b, i);
107 handlers [i].len = offset - handlers [i].start;
109 handlers = new ILExceptionBlock [1];
110 len = offset - start;
115 internal struct ILTokenInfo {
116 public MemberInfo member;
120 internal interface TokenGenerator {
121 int GetToken (string str);
123 int GetToken (MemberInfo member);
125 int GetToken (MethodInfo method, Type[] opt_param_types);
127 int GetToken (SignatureHelper helper);
130 public class ILGenerator: Object {
131 private struct LabelFixup {
132 public int offset; // The number of bytes between pos and the
133 // offset of the jump
134 public int pos; // Where offset of the label is placed
135 public int label_idx; // The label to jump to
139 public LabelData (int addr, int maxStack)
142 this.maxStack = maxStack;
149 static readonly Type void_type = typeof (void);
150 #region Sync with reflection.h
152 private int code_len;
153 private int max_stack;
154 private int cur_stack;
155 private LocalBuilder[] locals;
156 private ILExceptionInfo[] ex_handlers;
157 private int num_token_fixups;
158 private ILTokenInfo[] token_fixups;
161 private LabelData [] labels;
162 private int num_labels;
163 private LabelFixup[] fixups;
164 private int num_fixups;
165 internal Module module;
166 internal IMonoSymbolWriter sym_writer;
167 private Stack scopes;
168 private int cur_block;
169 private Stack open_blocks;
170 private TokenGenerator token_gen;
172 const int defaultFixupSize = 8;
173 const int defaultLabelsSize = 8;
175 internal ILGenerator (Module m, TokenGenerator token_gen, int size)
180 code = new byte [size];
181 cur_stack = max_stack = 0;
182 num_fixups = num_labels = 0;
183 token_fixups = new ILTokenInfo [8];
184 num_token_fixups = 0;
186 if (module is ModuleBuilder)
187 sym_writer = ((ModuleBuilder)module).symbol_writer;
188 open_blocks = new Stack ();
189 this.token_gen = token_gen;
192 private void add_token_fixup (MemberInfo mi)
194 if (num_token_fixups == token_fixups.Length) {
195 ILTokenInfo[] ntf = new ILTokenInfo [num_token_fixups * 2];
196 token_fixups.CopyTo (ntf, 0);
199 token_fixups [num_token_fixups].member = mi;
200 token_fixups [num_token_fixups++].code_pos = code_len;
203 private void make_room (int nbytes)
205 if (code_len + nbytes < code.Length)
207 byte[] new_code = new byte [(code_len + nbytes) * 2 + 128];
208 System.Array.Copy (code, 0, new_code, 0, code.Length);
212 private void emit_int (int val)
214 code [code_len++] = (byte) (val & 0xFF);
215 code [code_len++] = (byte) ((val >> 8) & 0xFF);
216 code [code_len++] = (byte) ((val >> 16) & 0xFF);
217 code [code_len++] = (byte) ((val >> 24) & 0xFF);
220 /* change to pass by ref to avoid copy */
221 private void ll_emit (OpCode opcode)
224 * there is already enough room allocated in code.
226 // access op1 and op2 directly since the Value property is useless
227 if (opcode.Size == 2)
228 code [code_len++] = opcode.op1;
229 code [code_len++] = opcode.op2;
231 * We should probably keep track of stack needs here.
232 * Or we may want to run the verifier on the code before saving it
233 * (this may be needed anyway when the ILGenerator is not used...).
235 switch (opcode.StackBehaviourPush) {
236 case StackBehaviour.Push1:
237 case StackBehaviour.Pushi:
238 case StackBehaviour.Pushi8:
239 case StackBehaviour.Pushr4:
240 case StackBehaviour.Pushr8:
241 case StackBehaviour.Pushref:
242 case StackBehaviour.Varpush: /* again we are conservative and assume it pushes 1 */
245 case StackBehaviour.Push1_push1:
249 if (max_stack < cur_stack)
250 max_stack = cur_stack;
253 * Note that we adjust for the pop behaviour _after_ setting max_stack.
255 switch (opcode.StackBehaviourPop) {
256 case StackBehaviour.Varpop:
257 break; /* we are conservative and assume it doesn't decrease the stack needs */
258 case StackBehaviour.Pop1:
259 case StackBehaviour.Popi:
260 case StackBehaviour.Popref:
263 case StackBehaviour.Pop1_pop1:
264 case StackBehaviour.Popi_pop1:
265 case StackBehaviour.Popi_popi:
266 case StackBehaviour.Popi_popi8:
267 case StackBehaviour.Popi_popr4:
268 case StackBehaviour.Popi_popr8:
269 case StackBehaviour.Popref_pop1:
270 case StackBehaviour.Popref_popi:
273 case StackBehaviour.Popi_popi_popi:
274 case StackBehaviour.Popref_popi_popi:
275 case StackBehaviour.Popref_popi_popi8:
276 case StackBehaviour.Popref_popi_popr4:
277 case StackBehaviour.Popref_popi_popr8:
278 case StackBehaviour.Popref_popi_popref:
284 private static int target_len (OpCode opcode)
286 if (opcode.OperandType == OperandType.InlineBrTarget)
291 private void InternalEndClause ()
293 switch (ex_handlers [cur_block].LastClauseType ()) {
294 case ILExceptionBlock.CATCH:
295 // how could we optimize code size here?
296 Emit (OpCodes.Leave, ex_handlers [cur_block].end);
298 case ILExceptionBlock.FAULT:
299 case ILExceptionBlock.FINALLY:
300 Emit (OpCodes.Endfinally);
302 case ILExceptionBlock.FILTER:
303 Emit (OpCodes.Endfilter);
308 public virtual void BeginCatchBlock (Type exceptionType)
310 if (open_blocks.Count <= 0)
311 throw new NotSupportedException ("Not in an exception block");
312 InternalEndClause ();
313 ex_handlers [cur_block].AddCatch (exceptionType, code_len);
314 cur_stack = 1; // the exception object is on the stack by default
315 if (max_stack < cur_stack)
316 max_stack = cur_stack;
317 //System.Console.WriteLine ("Begin catch Block: {0} {1}",exceptionType.ToString(), max_stack);
318 //throw new NotImplementedException ();
322 public virtual void BeginExceptFilterBlock ()
324 throw new NotImplementedException ();
327 public virtual Label BeginExceptionBlock ()
329 //System.Console.WriteLine ("Begin Block");
331 if (ex_handlers != null) {
332 cur_block = ex_handlers.Length;
333 ILExceptionInfo[] new_ex = new ILExceptionInfo [cur_block + 1];
334 System.Array.Copy (ex_handlers, new_ex, cur_block);
335 ex_handlers = new_ex;
337 ex_handlers = new ILExceptionInfo [1];
340 open_blocks.Push (cur_block);
341 ex_handlers [cur_block].start = code_len;
342 return ex_handlers [cur_block].end = DefineLabel ();
345 public virtual void BeginFaultBlock()
347 if (open_blocks.Count <= 0)
348 throw new NotSupportedException ("Not in an exception block");
349 //System.Console.WriteLine ("Begin fault Block");
350 //throw new NotImplementedException ();
353 public virtual void BeginFinallyBlock()
355 if (open_blocks.Count <= 0)
356 throw new NotSupportedException ("Not in an exception block");
357 InternalEndClause ();
358 //System.Console.WriteLine ("Begin finally Block");
359 ex_handlers [cur_block].AddFinally (code_len);
362 public virtual void BeginScope ()
364 if (sym_writer != null) {
366 scopes = new Stack ();
367 scopes.Push (sym_writer.OpenScope (code_len));
371 public LocalBuilder DeclareLocal (Type localType)
373 LocalBuilder res = new LocalBuilder (localType, this);
374 if (locals != null) {
375 LocalBuilder[] new_l = new LocalBuilder [locals.Length + 1];
376 System.Array.Copy (locals, new_l, locals.Length);
377 new_l [locals.Length] = res;
380 locals = new LocalBuilder [1];
383 res.position = (ushort)(locals.Length - 1);
387 public virtual Label DefineLabel ()
390 labels = new LabelData [defaultLabelsSize];
391 else if (num_labels >= labels.Length) {
392 LabelData [] t = new LabelData [labels.Length * 2];
393 Array.Copy (labels, t, labels.Length);
397 labels [num_labels] = new LabelData (-1, 0);
399 return new Label (num_labels++);
402 public virtual void Emit (OpCode opcode)
408 public virtual void Emit (OpCode opcode, Byte val)
412 code [code_len++] = val;
415 public virtual void Emit (OpCode opcode, ConstructorInfo constructor)
417 int token = token_gen.GetToken (constructor);
420 if (constructor.DeclaringType.Module == module)
421 add_token_fixup (constructor);
424 if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
425 cur_stack -= constructor.GetParameterCount ();
428 public virtual void Emit (OpCode opcode, double val)
430 Double.AssertEndianity (out val);
432 byte[] s = System.BitConverter.GetBytes (val);
435 if (BitConverter.IsLittleEndian){
436 System.Array.Copy (s, 0, code, code_len, 8);
439 code [code_len++] = s [7];
440 code [code_len++] = s [6];
441 code [code_len++] = s [5];
442 code [code_len++] = s [4];
443 code [code_len++] = s [3];
444 code [code_len++] = s [2];
445 code [code_len++] = s [1];
446 code [code_len++] = s [0];
450 public virtual void Emit (OpCode opcode, FieldInfo field)
452 int token = token_gen.GetToken (field);
455 if (field.DeclaringType.Module == module)
456 add_token_fixup (field);
460 public virtual void Emit (OpCode opcode, Int16 val)
464 code [code_len++] = (byte) (val & 0xFF);
465 code [code_len++] = (byte) ((val >> 8) & 0xFF);
468 public virtual void Emit (OpCode opcode, int val)
475 public virtual void Emit (OpCode opcode, long val)
479 code [code_len++] = (byte) (val & 0xFF);
480 code [code_len++] = (byte) ((val >> 8) & 0xFF);
481 code [code_len++] = (byte) ((val >> 16) & 0xFF);
482 code [code_len++] = (byte) ((val >> 24) & 0xFF);
483 code [code_len++] = (byte) ((val >> 32) & 0xFF);
484 code [code_len++] = (byte) ((val >> 40) & 0xFF);
485 code [code_len++] = (byte) ((val >> 48) & 0xFF);
486 code [code_len++] = (byte) ((val >> 56) & 0xFF);
489 public virtual void Emit (OpCode opcode, Label label)
491 int tlen = target_len (opcode);
494 if (cur_stack > labels [label.label].maxStack)
495 labels [label.label].maxStack = cur_stack;
498 fixups = new LabelFixup [defaultFixupSize];
499 else if (num_fixups >= fixups.Length) {
500 LabelFixup[] newf = new LabelFixup [fixups.Length + 16];
501 System.Array.Copy (fixups, newf, fixups.Length);
504 fixups [num_fixups].offset = tlen;
505 fixups [num_fixups].pos = code_len;
506 fixups [num_fixups].label_idx = label.label;
512 public virtual void Emit (OpCode opcode, Label[] labels)
514 /* opcode needs to be switch. */
515 int count = labels.Length;
516 make_room (6 + count * 4);
519 for (int i = 0; i < count; ++i)
520 if (cur_stack > this.labels [labels [i].label].maxStack)
521 this.labels [labels [i].label].maxStack = cur_stack;
525 fixups = new LabelFixup [defaultFixupSize + count];
526 else if (num_fixups + count >= fixups.Length) {
527 LabelFixup[] newf = new LabelFixup [fixups.Length + count + 16];
528 System.Array.Copy (fixups, newf, fixups.Length);
532 // ECMA 335, Partition III, p94 (7-10)
534 // The switch instruction implements a jump table. The format of
535 // the instruction is an unsigned int32 representing the number of targets N,
536 // followed by N int32 values specifying jump targets: these targets are
537 // represented as offsets (positive or negative) from the beginning of the
538 // instruction following this switch instruction.
540 // We must make sure it gets an offset from the *end* of the last label
541 // (eg, the beginning of the instruction following this).
543 // remaining is the number of bytes from the current instruction to the
544 // instruction that will be emitted.
546 for (int i = 0, remaining = count * 4; i < count; ++i, remaining -= 4) {
547 fixups [num_fixups].offset = remaining;
548 fixups [num_fixups].pos = code_len;
549 fixups [num_fixups].label_idx = labels [i].label;
555 public virtual void Emit (OpCode opcode, LocalBuilder lbuilder)
557 uint pos = lbuilder.position;
558 bool load_addr = false;
559 bool is_store = false;
562 if (lbuilder.ilgen != this)
563 throw new Exception ("Trying to emit a local from a different ILGenerator.");
565 /* inline the code from ll_emit () to optimize il code size */
566 if (opcode.StackBehaviourPop == StackBehaviour.Pop1) {
571 if (cur_stack > max_stack)
572 max_stack = cur_stack;
573 load_addr = opcode.StackBehaviourPush == StackBehaviour.Pushi;
577 code [code_len++] = (byte)0x12;
578 code [code_len++] = (byte)pos;
580 code [code_len++] = (byte)0xfe;
581 code [code_len++] = (byte)0x0d;
582 code [code_len++] = (byte)(pos & 0xff);
583 code [code_len++] = (byte)((pos >> 8) & 0xff);
588 code [code_len++] = (byte)(0x0a + pos);
589 } else if (pos < 256) {
590 code [code_len++] = (byte)0x13;
591 code [code_len++] = (byte)pos;
593 code [code_len++] = (byte)0xfe;
594 code [code_len++] = (byte)0x0e;
595 code [code_len++] = (byte)(pos & 0xff);
596 code [code_len++] = (byte)((pos >> 8) & 0xff);
600 code [code_len++] = (byte)(0x06 + pos);
601 } else if (pos < 256) {
602 code [code_len++] = (byte)0x11;
603 code [code_len++] = (byte)pos;
605 code [code_len++] = (byte)0xfe;
606 code [code_len++] = (byte)0x0c;
607 code [code_len++] = (byte)(pos & 0xff);
608 code [code_len++] = (byte)((pos >> 8) & 0xff);
614 public virtual void Emit (OpCode opcode, MethodInfo method)
617 throw new ArgumentNullException ("method");
619 int token = token_gen.GetToken (method);
622 if (method.DeclaringType.Module == module)
623 add_token_fixup (method);
625 if (method.ReturnType != void_type)
628 if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
629 cur_stack -= method.GetParameterCount ();
632 private void Emit (OpCode opcode, MethodInfo method, int token)
636 if (method.DeclaringType.Module == module)
637 add_token_fixup (method);
639 if (method.ReturnType != void_type)
642 if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
643 cur_stack -= method.GetParameterCount ();
646 [CLSCompliant(false)]
647 public void Emit (OpCode opcode, sbyte val)
651 code [code_len++] = (byte)val;
654 public virtual void Emit (OpCode opcode, SignatureHelper shelper)
656 int token = token_gen.GetToken (shelper);
662 public virtual void Emit (OpCode opcode, float val)
664 byte[] s = System.BitConverter.GetBytes (val);
667 if (BitConverter.IsLittleEndian){
668 System.Array.Copy (s, 0, code, code_len, 4);
671 code [code_len++] = s [3];
672 code [code_len++] = s [2];
673 code [code_len++] = s [1];
674 code [code_len++] = s [0];
678 public virtual void Emit (OpCode opcode, string val)
680 int token = token_gen.GetToken (val);
686 public virtual void Emit (OpCode opcode, Type type)
690 emit_int (token_gen.GetToken (type));
693 [MonoTODO ("Do something about varargs method")]
694 public void EmitCall (OpCode opcode, MethodInfo methodinfo, Type[] optionalParamTypes)
696 if (methodinfo == null)
697 throw new ArgumentNullException ("methodinfo can not be null");
698 short value = opcode.Value;
699 if (!(value == OpCodes.Call.Value || value == OpCodes.Callvirt.Value))
700 throw new NotSupportedException ("Only Call and CallVirt are allowed");
701 if (optionalParamTypes != null){
702 if ((methodinfo.CallingConvention & CallingConventions.VarArgs) == 0){
703 throw new InvalidOperationException ("Method is not VarArgs method and optional types were passed");
706 int token = token_gen.GetToken (methodinfo, optionalParamTypes);
707 Emit (opcode, methodinfo, token);
710 Emit (opcode, methodinfo);
713 public void EmitCalli (OpCode opcode, CallingConvention unmanagedCallConv, Type returnType, Type[] paramTypes)
715 SignatureHelper helper
716 = SignatureHelper.GetMethodSigHelper (module, 0, unmanagedCallConv, returnType, paramTypes);
717 Emit (opcode, helper);
720 public void EmitCalli (OpCode opcode, CallingConventions callConv, Type returnType, Type[] paramTypes, Type[] optionalParamTypes)
722 if (optionalParamTypes != null)
723 throw new NotImplementedException ();
725 SignatureHelper helper
726 = SignatureHelper.GetMethodSigHelper (module, callConv, 0, returnType, paramTypes);
727 Emit (opcode, helper);
730 public virtual void EmitWriteLine (FieldInfo field)
733 throw new ArgumentNullException ("field");
735 // The MS implementation does not check for valuetypes here but it
736 // should. Also, it should check that if the field is not static,
737 // then it is a member of this type.
739 Emit (OpCodes.Ldsfld, field);
741 Emit (OpCodes.Ldarg_0);
742 Emit (OpCodes.Ldfld, field);
745 typeof (Console).GetMethod ("WriteLine",
746 new Type[1] { field.FieldType }));
749 public virtual void EmitWriteLine (LocalBuilder lbuilder)
751 if (lbuilder == null)
752 throw new ArgumentNullException ("lbuilder");
753 if (lbuilder.LocalType is TypeBuilder)
754 throw new ArgumentException ("Output streams do not support TypeBuilders.");
755 // The MS implementation does not check for valuetypes here but it
757 Emit (OpCodes.Ldloc, lbuilder);
759 typeof (Console).GetMethod ("WriteLine",
760 new Type[1] { lbuilder.LocalType }));
763 public virtual void EmitWriteLine (string val)
765 Emit (OpCodes.Ldstr, val);
767 typeof (Console).GetMethod ("WriteLine",
768 new Type[1] { typeof(string)}));
771 public virtual void EndExceptionBlock ()
773 if (open_blocks.Count <= 0)
774 throw new NotSupportedException ("Not in an exception block");
775 InternalEndClause ();
776 MarkLabel (ex_handlers [cur_block].end);
777 ex_handlers [cur_block].End (code_len);
778 ex_handlers [cur_block].Debug (cur_block);
779 //System.Console.WriteLine ("End Block {0} (handlers: {1})", cur_block, ex_handlers [cur_block].NumHandlers ());
781 if (open_blocks.Count > 0)
782 cur_block = (int)open_blocks.Peek ();
783 //Console.WriteLine ("curblock restored to {0}", cur_block);
784 //throw new NotImplementedException ();
787 public virtual void EndScope ()
789 if (sym_writer != null) {
790 sym_writer.CloseScope (code_len);
792 throw new InvalidOperationException ();
797 public virtual void MarkLabel (Label loc)
799 if (loc.label < 0 || loc.label >= num_labels)
800 throw new System.ArgumentException ("The label is not valid");
801 if (labels [loc.label].addr >= 0)
802 throw new System.ArgumentException ("The label was already defined");
803 labels [loc.label].addr = code_len;
804 if (labels [loc.label].maxStack > cur_stack)
805 cur_stack = labels [loc.label].maxStack;
808 public virtual void MarkSequencePoint (ISymbolDocumentWriter document, int startLine,
809 int startColumn, int endLine, int endColumn)
811 if (sym_writer == null)
814 sym_writer.MarkSequencePoint (code_len, startLine, startColumn);
817 public virtual void ThrowException (Type exceptionType)
819 if (exceptionType == null)
820 throw new ArgumentNullException ("exceptionType");
821 if (! ((exceptionType == typeof (Exception)) ||
822 exceptionType.IsSubclassOf (typeof (Exception))))
823 throw new ArgumentException ("Type should be an exception type", "exceptionType");
824 ConstructorInfo ctor = exceptionType.GetConstructor (new Type[0]);
826 throw new ArgumentException ("Type should have a default constructor", "exceptionType");
827 Emit (OpCodes.Newobj, ctor);
828 Emit (OpCodes.Throw);
832 public void UsingNamespace (String usingNamespace)
834 throw new NotImplementedException ();
837 internal void label_fixup ()
839 for (int i = 0; i < num_fixups; ++i) {
841 // Diff is the offset from the end of the jump instruction to the address of the label
842 int diff = labels [fixups [i].label_idx].addr - (fixups [i].pos + fixups [i].offset);
843 if (fixups [i].offset == 1) {
844 code [fixups [i].pos] = (byte)((sbyte) diff);
846 int old_cl = code_len;
847 code_len = fixups [i].pos;