3 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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:
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
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.
26 // System.Reflection.Emit/ILGenerator.cs
29 // Paolo Molaro (lupus@ximian.com)
31 // (C) 2001 Ximian, Inc. http://www.ximian.com
35 using System.Collections;
36 using System.Diagnostics.SymbolStore;
37 using System.Runtime.InteropServices;
39 namespace System.Reflection.Emit {
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;
51 internal int filter_offset;
53 internal void Debug () {
55 System.Console.Write ("\ttype="+type.ToString()+" start="+start.ToString()+" len="+len.ToString());
57 System.Console.WriteLine (" extype="+extype.ToString());
59 System.Console.WriteLine ("");
63 internal struct ILExceptionInfo {
64 ILExceptionBlock[] handlers;
69 internal int NumHandlers ()
71 return handlers.Length;
74 internal void AddCatch (Type extype, int offset)
79 i = handlers.Length - 1;
80 handlers [i].type = ILExceptionBlock.CATCH;
81 handlers [i].start = offset;
82 handlers [i].extype = extype;
85 internal void AddFinally (int offset)
90 i = handlers.Length - 1;
91 handlers [i].type = ILExceptionBlock.FINALLY;
92 handlers [i].start = offset;
93 handlers [i].extype = null;
96 internal void AddFault (int offset)
101 i = handlers.Length - 1;
102 handlers [i].type = ILExceptionBlock.FAULT;
103 handlers [i].start = offset;
104 handlers [i].extype = null;
107 internal void End (int offset)
109 if (handlers == null)
111 int i = handlers.Length - 1;
113 handlers [i].len = offset - handlers [i].start;
116 internal int LastClauseType ()
118 if (handlers != null)
119 return handlers [handlers.Length-1].type;
121 return ILExceptionBlock.CATCH;
124 internal void Debug (int b)
127 System.Console.WriteLine ("Handler {0} at {1}, len: {2}", b, start, len);
128 for (int i = 0; i < handlers.Length; ++i)
129 handlers [i].Debug ();
133 void add_block (int offset)
135 if (handlers != null) {
136 int i = handlers.Length;
137 ILExceptionBlock[] new_b = new ILExceptionBlock [i + 1];
138 System.Array.Copy (handlers, new_b, i);
140 handlers [i].len = offset - handlers [i].start;
142 handlers = new ILExceptionBlock [1];
143 len = offset - start;
148 internal struct ILTokenInfo {
149 public MemberInfo member;
153 internal interface TokenGenerator {
154 int GetToken (string str);
156 int GetToken (MemberInfo member);
158 int GetToken (MethodInfo method, Type[] opt_param_types);
160 int GetToken (SignatureHelper helper);
163 public class ILGenerator: Object {
164 private struct LabelFixup {
165 public int offset; // The number of bytes between pos and the
166 // offset of the jump
167 public int pos; // Where offset of the label is placed
168 public int label_idx; // The label to jump to
172 public LabelData (int addr, int maxStack)
175 this.maxStack = maxStack;
182 static readonly Type void_type = typeof (void);
183 #region Sync with reflection.h
185 private int code_len;
186 private int max_stack;
187 private int cur_stack;
188 private LocalBuilder[] locals;
189 private ILExceptionInfo[] ex_handlers;
190 private int num_token_fixups;
191 private ILTokenInfo[] token_fixups;
194 private LabelData [] labels;
195 private int num_labels;
196 private LabelFixup[] fixups;
197 private int num_fixups;
198 internal Module module;
199 private Stack scopes;
200 private int cur_block;
201 private Stack open_blocks;
202 private TokenGenerator token_gen;
204 const int defaultFixupSize = 8;
205 const int defaultLabelsSize = 8;
207 internal ILGenerator (Module m, TokenGenerator token_gen, int size)
212 code = new byte [size];
213 cur_stack = max_stack = 0;
214 num_fixups = num_labels = 0;
215 token_fixups = new ILTokenInfo [8];
216 num_token_fixups = 0;
218 open_blocks = new Stack ();
219 this.token_gen = token_gen;
222 private void add_token_fixup (MemberInfo mi)
224 if (num_token_fixups == token_fixups.Length) {
225 ILTokenInfo[] ntf = new ILTokenInfo [num_token_fixups * 2];
226 token_fixups.CopyTo (ntf, 0);
229 token_fixups [num_token_fixups].member = mi;
230 token_fixups [num_token_fixups++].code_pos = code_len;
233 private void make_room (int nbytes)
235 if (code_len + nbytes < code.Length)
237 byte[] new_code = new byte [(code_len + nbytes) * 2 + 128];
238 System.Array.Copy (code, 0, new_code, 0, code.Length);
242 private void emit_int (int val)
244 code [code_len++] = (byte) (val & 0xFF);
245 code [code_len++] = (byte) ((val >> 8) & 0xFF);
246 code [code_len++] = (byte) ((val >> 16) & 0xFF);
247 code [code_len++] = (byte) ((val >> 24) & 0xFF);
250 /* change to pass by ref to avoid copy */
251 private void ll_emit (OpCode opcode)
254 * there is already enough room allocated in code.
256 // access op1 and op2 directly since the Value property is useless
257 if (opcode.Size == 2)
258 code [code_len++] = opcode.op1;
259 code [code_len++] = opcode.op2;
261 * We should probably keep track of stack needs here.
262 * Or we may want to run the verifier on the code before saving it
263 * (this may be needed anyway when the ILGenerator is not used...).
265 switch (opcode.StackBehaviourPush) {
266 case StackBehaviour.Push1:
267 case StackBehaviour.Pushi:
268 case StackBehaviour.Pushi8:
269 case StackBehaviour.Pushr4:
270 case StackBehaviour.Pushr8:
271 case StackBehaviour.Pushref:
272 case StackBehaviour.Varpush: /* again we are conservative and assume it pushes 1 */
275 case StackBehaviour.Push1_push1:
279 if (max_stack < cur_stack)
280 max_stack = cur_stack;
283 * Note that we adjust for the pop behaviour _after_ setting max_stack.
285 switch (opcode.StackBehaviourPop) {
286 case StackBehaviour.Varpop:
287 break; /* we are conservative and assume it doesn't decrease the stack needs */
288 case StackBehaviour.Pop1:
289 case StackBehaviour.Popi:
290 case StackBehaviour.Popref:
293 case StackBehaviour.Pop1_pop1:
294 case StackBehaviour.Popi_pop1:
295 case StackBehaviour.Popi_popi:
296 case StackBehaviour.Popi_popi8:
297 case StackBehaviour.Popi_popr4:
298 case StackBehaviour.Popi_popr8:
299 case StackBehaviour.Popref_pop1:
300 case StackBehaviour.Popref_popi:
303 case StackBehaviour.Popi_popi_popi:
304 case StackBehaviour.Popref_popi_popi:
305 case StackBehaviour.Popref_popi_popi8:
306 case StackBehaviour.Popref_popi_popr4:
307 case StackBehaviour.Popref_popi_popr8:
308 case StackBehaviour.Popref_popi_popref:
314 private static int target_len (OpCode opcode)
316 if (opcode.OperandType == OperandType.InlineBrTarget)
321 private void InternalEndClause ()
323 switch (ex_handlers [cur_block].LastClauseType ()) {
324 case ILExceptionBlock.CATCH:
325 // how could we optimize code size here?
326 Emit (OpCodes.Leave, ex_handlers [cur_block].end);
328 case ILExceptionBlock.FAULT:
329 case ILExceptionBlock.FINALLY:
330 Emit (OpCodes.Endfinally);
332 case ILExceptionBlock.FILTER:
333 Emit (OpCodes.Endfilter);
338 public virtual void BeginCatchBlock (Type exceptionType)
340 if (open_blocks.Count <= 0)
341 throw new NotSupportedException ("Not in an exception block");
342 InternalEndClause ();
343 ex_handlers [cur_block].AddCatch (exceptionType, code_len);
344 cur_stack = 1; // the exception object is on the stack by default
345 if (max_stack < cur_stack)
346 max_stack = cur_stack;
347 //System.Console.WriteLine ("Begin catch Block: {0} {1}",exceptionType.ToString(), max_stack);
348 //throw new NotImplementedException ();
352 public virtual void BeginExceptFilterBlock ()
354 throw new NotImplementedException ();
357 public virtual Label BeginExceptionBlock ()
359 //System.Console.WriteLine ("Begin Block");
361 if (ex_handlers != null) {
362 cur_block = ex_handlers.Length;
363 ILExceptionInfo[] new_ex = new ILExceptionInfo [cur_block + 1];
364 System.Array.Copy (ex_handlers, new_ex, cur_block);
365 ex_handlers = new_ex;
367 ex_handlers = new ILExceptionInfo [1];
370 open_blocks.Push (cur_block);
371 ex_handlers [cur_block].start = code_len;
372 return ex_handlers [cur_block].end = DefineLabel ();
375 public virtual void BeginFaultBlock()
377 if (open_blocks.Count <= 0)
378 throw new NotSupportedException ("Not in an exception block");
379 //System.Console.WriteLine ("Begin fault Block");
380 ex_handlers [cur_block].AddFault (code_len);
383 public virtual void BeginFinallyBlock()
385 if (open_blocks.Count <= 0)
386 throw new NotSupportedException ("Not in an exception block");
387 InternalEndClause ();
388 //System.Console.WriteLine ("Begin finally Block");
389 ex_handlers [cur_block].AddFinally (code_len);
392 public virtual void BeginScope ()
395 public LocalBuilder DeclareLocal (Type localType)
397 return DeclareLocal (localType, false);
406 LocalBuilder DeclareLocal (Type localType, bool pinned)
408 if (localType == null)
409 throw new ArgumentNullException ("localType");
411 LocalBuilder res = new LocalBuilder (localType, this);
412 res.is_pinned = pinned;
414 if (locals != null) {
415 LocalBuilder[] new_l = new LocalBuilder [locals.Length + 1];
416 System.Array.Copy (locals, new_l, locals.Length);
417 new_l [locals.Length] = res;
420 locals = new LocalBuilder [1];
423 res.position = (ushort)(locals.Length - 1);
427 public virtual Label DefineLabel ()
430 labels = new LabelData [defaultLabelsSize];
431 else if (num_labels >= labels.Length) {
432 LabelData [] t = new LabelData [labels.Length * 2];
433 Array.Copy (labels, t, labels.Length);
437 labels [num_labels] = new LabelData (-1, 0);
439 return new Label (num_labels++);
442 public virtual void Emit (OpCode opcode)
448 public virtual void Emit (OpCode opcode, Byte val)
452 code [code_len++] = val;
455 public virtual void Emit (OpCode opcode, ConstructorInfo constructor)
457 int token = token_gen.GetToken (constructor);
460 if (constructor.DeclaringType.Module == module)
461 add_token_fixup (constructor);
464 if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
465 cur_stack -= constructor.GetParameterCount ();
468 public virtual void Emit (OpCode opcode, double val)
470 Double.AssertEndianity (out val);
472 byte[] s = System.BitConverter.GetBytes (val);
475 if (BitConverter.IsLittleEndian){
476 System.Array.Copy (s, 0, code, code_len, 8);
479 code [code_len++] = s [7];
480 code [code_len++] = s [6];
481 code [code_len++] = s [5];
482 code [code_len++] = s [4];
483 code [code_len++] = s [3];
484 code [code_len++] = s [2];
485 code [code_len++] = s [1];
486 code [code_len++] = s [0];
490 public virtual void Emit (OpCode opcode, FieldInfo field)
492 int token = token_gen.GetToken (field);
495 if (field.DeclaringType.Module == module)
496 add_token_fixup (field);
500 public virtual void Emit (OpCode opcode, Int16 val)
504 code [code_len++] = (byte) (val & 0xFF);
505 code [code_len++] = (byte) ((val >> 8) & 0xFF);
508 public virtual void Emit (OpCode opcode, int val)
515 public virtual void Emit (OpCode opcode, long val)
519 code [code_len++] = (byte) (val & 0xFF);
520 code [code_len++] = (byte) ((val >> 8) & 0xFF);
521 code [code_len++] = (byte) ((val >> 16) & 0xFF);
522 code [code_len++] = (byte) ((val >> 24) & 0xFF);
523 code [code_len++] = (byte) ((val >> 32) & 0xFF);
524 code [code_len++] = (byte) ((val >> 40) & 0xFF);
525 code [code_len++] = (byte) ((val >> 48) & 0xFF);
526 code [code_len++] = (byte) ((val >> 56) & 0xFF);
529 public virtual void Emit (OpCode opcode, Label label)
531 int tlen = target_len (opcode);
534 if (cur_stack > labels [label.label].maxStack)
535 labels [label.label].maxStack = cur_stack;
538 fixups = new LabelFixup [defaultFixupSize];
539 else if (num_fixups >= fixups.Length) {
540 LabelFixup[] newf = new LabelFixup [fixups.Length + 16];
541 System.Array.Copy (fixups, newf, fixups.Length);
544 fixups [num_fixups].offset = tlen;
545 fixups [num_fixups].pos = code_len;
546 fixups [num_fixups].label_idx = label.label;
552 public virtual void Emit (OpCode opcode, Label[] labels)
554 /* opcode needs to be switch. */
555 int count = labels.Length;
556 make_room (6 + count * 4);
559 for (int i = 0; i < count; ++i)
560 if (cur_stack > this.labels [labels [i].label].maxStack)
561 this.labels [labels [i].label].maxStack = cur_stack;
565 fixups = new LabelFixup [defaultFixupSize + count];
566 else if (num_fixups + count >= fixups.Length) {
567 LabelFixup[] newf = new LabelFixup [fixups.Length + count + 16];
568 System.Array.Copy (fixups, newf, fixups.Length);
572 // ECMA 335, Partition III, p94 (7-10)
574 // The switch instruction implements a jump table. The format of
575 // the instruction is an unsigned int32 representing the number of targets N,
576 // followed by N int32 values specifying jump targets: these targets are
577 // represented as offsets (positive or negative) from the beginning of the
578 // instruction following this switch instruction.
580 // We must make sure it gets an offset from the *end* of the last label
581 // (eg, the beginning of the instruction following this).
583 // remaining is the number of bytes from the current instruction to the
584 // instruction that will be emitted.
586 for (int i = 0, remaining = count * 4; i < count; ++i, remaining -= 4) {
587 fixups [num_fixups].offset = remaining;
588 fixups [num_fixups].pos = code_len;
589 fixups [num_fixups].label_idx = labels [i].label;
595 public virtual void Emit (OpCode opcode, LocalBuilder lbuilder)
597 uint pos = lbuilder.position;
598 bool load_addr = false;
599 bool is_store = false;
602 if (lbuilder.ilgen != this)
603 throw new Exception ("Trying to emit a local from a different ILGenerator.");
605 /* inline the code from ll_emit () to optimize il code size */
606 if (opcode.StackBehaviourPop == StackBehaviour.Pop1) {
611 if (cur_stack > max_stack)
612 max_stack = cur_stack;
613 load_addr = opcode.StackBehaviourPush == StackBehaviour.Pushi;
617 code [code_len++] = (byte)0x12;
618 code [code_len++] = (byte)pos;
620 code [code_len++] = (byte)0xfe;
621 code [code_len++] = (byte)0x0d;
622 code [code_len++] = (byte)(pos & 0xff);
623 code [code_len++] = (byte)((pos >> 8) & 0xff);
628 code [code_len++] = (byte)(0x0a + pos);
629 } else if (pos < 256) {
630 code [code_len++] = (byte)0x13;
631 code [code_len++] = (byte)pos;
633 code [code_len++] = (byte)0xfe;
634 code [code_len++] = (byte)0x0e;
635 code [code_len++] = (byte)(pos & 0xff);
636 code [code_len++] = (byte)((pos >> 8) & 0xff);
640 code [code_len++] = (byte)(0x06 + pos);
641 } else if (pos < 256) {
642 code [code_len++] = (byte)0x11;
643 code [code_len++] = (byte)pos;
645 code [code_len++] = (byte)0xfe;
646 code [code_len++] = (byte)0x0c;
647 code [code_len++] = (byte)(pos & 0xff);
648 code [code_len++] = (byte)((pos >> 8) & 0xff);
654 public virtual void Emit (OpCode opcode, MethodInfo method)
657 throw new ArgumentNullException ("method");
659 int token = token_gen.GetToken (method);
662 if (method.DeclaringType.Module == module)
663 add_token_fixup (method);
665 if (method.ReturnType != void_type)
668 if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
669 cur_stack -= method.GetParameterCount ();
672 private void Emit (OpCode opcode, MethodInfo method, int token)
676 if (method.DeclaringType.Module == module)
677 add_token_fixup (method);
679 if (method.ReturnType != void_type)
682 if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
683 cur_stack -= method.GetParameterCount ();
686 [CLSCompliant(false)]
687 public void Emit (OpCode opcode, sbyte val)
691 code [code_len++] = (byte)val;
694 public virtual void Emit (OpCode opcode, SignatureHelper shelper)
696 int token = token_gen.GetToken (shelper);
702 public virtual void Emit (OpCode opcode, float val)
704 byte[] s = System.BitConverter.GetBytes (val);
707 if (BitConverter.IsLittleEndian){
708 System.Array.Copy (s, 0, code, code_len, 4);
711 code [code_len++] = s [3];
712 code [code_len++] = s [2];
713 code [code_len++] = s [1];
714 code [code_len++] = s [0];
718 public virtual void Emit (OpCode opcode, string val)
720 int token = token_gen.GetToken (val);
726 public virtual void Emit (OpCode opcode, Type type)
730 emit_int (token_gen.GetToken (type));
733 [MonoTODO ("Do something about varargs method")]
734 public void EmitCall (OpCode opcode, MethodInfo methodinfo, Type[] optionalParamTypes)
736 if (methodinfo == null)
737 throw new ArgumentNullException ("methodinfo can not be null");
738 short value = opcode.Value;
739 if (!(value == OpCodes.Call.Value || value == OpCodes.Callvirt.Value))
740 throw new NotSupportedException ("Only Call and CallVirt are allowed");
741 if (optionalParamTypes != null){
742 if ((methodinfo.CallingConvention & CallingConventions.VarArgs) == 0){
743 throw new InvalidOperationException ("Method is not VarArgs method and optional types were passed");
746 int token = token_gen.GetToken (methodinfo, optionalParamTypes);
747 Emit (opcode, methodinfo, token);
750 Emit (opcode, methodinfo);
753 public void EmitCalli (OpCode opcode, CallingConvention unmanagedCallConv, Type returnType, Type[] paramTypes)
755 SignatureHelper helper
756 = SignatureHelper.GetMethodSigHelper (module, 0, unmanagedCallConv, returnType, paramTypes);
757 Emit (opcode, helper);
760 public void EmitCalli (OpCode opcode, CallingConventions callConv, Type returnType, Type[] paramTypes, Type[] optionalParamTypes)
762 if (optionalParamTypes != null)
763 throw new NotImplementedException ();
765 SignatureHelper helper
766 = SignatureHelper.GetMethodSigHelper (module, callConv, 0, returnType, paramTypes);
767 Emit (opcode, helper);
770 public virtual void EmitWriteLine (FieldInfo field)
773 throw new ArgumentNullException ("field");
775 // The MS implementation does not check for valuetypes here but it
776 // should. Also, it should check that if the field is not static,
777 // then it is a member of this type.
779 Emit (OpCodes.Ldsfld, field);
781 Emit (OpCodes.Ldarg_0);
782 Emit (OpCodes.Ldfld, field);
785 typeof (Console).GetMethod ("WriteLine",
786 new Type[1] { field.FieldType }));
789 public virtual void EmitWriteLine (LocalBuilder lbuilder)
791 if (lbuilder == null)
792 throw new ArgumentNullException ("lbuilder");
793 if (lbuilder.LocalType is TypeBuilder)
794 throw new ArgumentException ("Output streams do not support TypeBuilders.");
795 // The MS implementation does not check for valuetypes here but it
797 Emit (OpCodes.Ldloc, lbuilder);
799 typeof (Console).GetMethod ("WriteLine",
800 new Type[1] { lbuilder.LocalType }));
803 public virtual void EmitWriteLine (string val)
805 Emit (OpCodes.Ldstr, val);
807 typeof (Console).GetMethod ("WriteLine",
808 new Type[1] { typeof(string)}));
811 public virtual void EndExceptionBlock ()
813 if (open_blocks.Count <= 0)
814 throw new NotSupportedException ("Not in an exception block");
815 InternalEndClause ();
816 MarkLabel (ex_handlers [cur_block].end);
817 ex_handlers [cur_block].End (code_len);
818 ex_handlers [cur_block].Debug (cur_block);
819 //System.Console.WriteLine ("End Block {0} (handlers: {1})", cur_block, ex_handlers [cur_block].NumHandlers ());
821 if (open_blocks.Count > 0)
822 cur_block = (int)open_blocks.Peek ();
823 //Console.WriteLine ("curblock restored to {0}", cur_block);
824 //throw new NotImplementedException ();
827 public virtual void EndScope ()
830 public virtual void MarkLabel (Label loc)
832 if (loc.label < 0 || loc.label >= num_labels)
833 throw new System.ArgumentException ("The label is not valid");
834 if (labels [loc.label].addr >= 0)
835 throw new System.ArgumentException ("The label was already defined");
836 labels [loc.label].addr = code_len;
837 if (labels [loc.label].maxStack > cur_stack)
838 cur_stack = labels [loc.label].maxStack;
841 public virtual void MarkSequencePoint (ISymbolDocumentWriter document, int startLine,
842 int startColumn, int endLine, int endColumn)
845 public virtual void ThrowException (Type exceptionType)
847 if (exceptionType == null)
848 throw new ArgumentNullException ("exceptionType");
849 if (! ((exceptionType == typeof (Exception)) ||
850 exceptionType.IsSubclassOf (typeof (Exception))))
851 throw new ArgumentException ("Type should be an exception type", "exceptionType");
852 ConstructorInfo ctor = exceptionType.GetConstructor (new Type[0]);
854 throw new ArgumentException ("Type should have a default constructor", "exceptionType");
855 Emit (OpCodes.Newobj, ctor);
856 Emit (OpCodes.Throw);
860 public void UsingNamespace (String usingNamespace)
862 throw new NotImplementedException ();
865 internal void label_fixup ()
867 for (int i = 0; i < num_fixups; ++i) {
869 // Diff is the offset from the end of the jump instruction to the address of the label
870 int diff = labels [fixups [i].label_idx].addr - (fixups [i].pos + fixups [i].offset);
871 if (fixups [i].offset == 1) {
872 code [fixups [i].pos] = (byte)((sbyte) diff);
874 int old_cl = code_len;
875 code_len = fixups [i].pos;
882 internal static int Mono_GetCurrentOffset (ILGenerator ig)