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;
38 using Mono.CSharp.Debugger;
40 namespace System.Reflection.Emit {
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;
52 internal int filter_offset;
54 internal void Debug () {
56 System.Console.Write ("\ttype="+type.ToString()+" start="+start.ToString()+" len="+len.ToString());
58 System.Console.WriteLine (" extype="+extype.ToString());
60 System.Console.WriteLine ("");
64 internal struct ILExceptionInfo {
65 ILExceptionBlock[] handlers;
70 internal int NumHandlers ()
72 return handlers.Length;
75 internal void AddCatch (Type extype, int offset)
80 i = handlers.Length - 1;
81 handlers [i].type = ILExceptionBlock.CATCH;
82 handlers [i].start = offset;
83 handlers [i].extype = extype;
86 internal void AddFinally (int offset)
91 i = handlers.Length - 1;
92 handlers [i].type = ILExceptionBlock.FINALLY;
93 handlers [i].start = offset;
94 handlers [i].extype = null;
97 internal void End (int offset)
101 int i = handlers.Length - 1;
103 handlers [i].len = offset - handlers [i].start;
106 internal int LastClauseType ()
108 if (handlers != null)
109 return handlers [handlers.Length-1].type;
111 return ILExceptionBlock.CATCH;
114 internal void Debug (int b)
117 System.Console.WriteLine ("Handler {0} at {1}, len: {2}", b, start, len);
118 for (int i = 0; i < handlers.Length; ++i)
119 handlers [i].Debug ();
123 void add_block (int offset)
125 if (handlers != null) {
126 int i = handlers.Length;
127 ILExceptionBlock[] new_b = new ILExceptionBlock [i + 1];
128 System.Array.Copy (handlers, new_b, i);
130 handlers [i].len = offset - handlers [i].start;
132 handlers = new ILExceptionBlock [1];
133 len = offset - start;
138 internal struct ILTokenInfo {
139 public MemberInfo member;
143 internal interface TokenGenerator {
144 int GetToken (string str);
146 int GetToken (MemberInfo member);
148 int GetToken (MethodInfo method, Type[] opt_param_types);
150 int GetToken (SignatureHelper helper);
153 public class ILGenerator: Object {
154 private struct LabelFixup {
155 public int offset; // The number of bytes between pos and the
156 // offset of the jump
157 public int pos; // Where offset of the label is placed
158 public int label_idx; // The label to jump to
162 public LabelData (int addr, int maxStack)
165 this.maxStack = maxStack;
172 static readonly Type void_type = typeof (void);
173 #region Sync with reflection.h
175 private int code_len;
176 private int max_stack;
177 private int cur_stack;
178 private LocalBuilder[] locals;
179 private ILExceptionInfo[] ex_handlers;
180 private int num_token_fixups;
181 private ILTokenInfo[] token_fixups;
184 private LabelData [] labels;
185 private int num_labels;
186 private LabelFixup[] fixups;
187 private int num_fixups;
188 internal Module module;
189 internal IMonoSymbolWriter sym_writer;
190 private Stack scopes;
191 private int cur_block;
192 private Stack open_blocks;
193 private TokenGenerator token_gen;
195 const int defaultFixupSize = 8;
196 const int defaultLabelsSize = 8;
198 internal ILGenerator (Module m, TokenGenerator token_gen, int size)
203 code = new byte [size];
204 cur_stack = max_stack = 0;
205 num_fixups = num_labels = 0;
206 token_fixups = new ILTokenInfo [8];
207 num_token_fixups = 0;
209 if (module is ModuleBuilder)
210 sym_writer = ((ModuleBuilder)module).symbol_writer;
211 open_blocks = new Stack ();
212 this.token_gen = token_gen;
215 private void add_token_fixup (MemberInfo mi)
217 if (num_token_fixups == token_fixups.Length) {
218 ILTokenInfo[] ntf = new ILTokenInfo [num_token_fixups * 2];
219 token_fixups.CopyTo (ntf, 0);
222 token_fixups [num_token_fixups].member = mi;
223 token_fixups [num_token_fixups++].code_pos = code_len;
226 private void make_room (int nbytes)
228 if (code_len + nbytes < code.Length)
230 byte[] new_code = new byte [(code_len + nbytes) * 2 + 128];
231 System.Array.Copy (code, 0, new_code, 0, code.Length);
235 private void emit_int (int val)
237 code [code_len++] = (byte) (val & 0xFF);
238 code [code_len++] = (byte) ((val >> 8) & 0xFF);
239 code [code_len++] = (byte) ((val >> 16) & 0xFF);
240 code [code_len++] = (byte) ((val >> 24) & 0xFF);
243 /* change to pass by ref to avoid copy */
244 private void ll_emit (OpCode opcode)
247 * there is already enough room allocated in code.
249 // access op1 and op2 directly since the Value property is useless
250 if (opcode.Size == 2)
251 code [code_len++] = opcode.op1;
252 code [code_len++] = opcode.op2;
254 * We should probably keep track of stack needs here.
255 * Or we may want to run the verifier on the code before saving it
256 * (this may be needed anyway when the ILGenerator is not used...).
258 switch (opcode.StackBehaviourPush) {
259 case StackBehaviour.Push1:
260 case StackBehaviour.Pushi:
261 case StackBehaviour.Pushi8:
262 case StackBehaviour.Pushr4:
263 case StackBehaviour.Pushr8:
264 case StackBehaviour.Pushref:
265 case StackBehaviour.Varpush: /* again we are conservative and assume it pushes 1 */
268 case StackBehaviour.Push1_push1:
272 if (max_stack < cur_stack)
273 max_stack = cur_stack;
276 * Note that we adjust for the pop behaviour _after_ setting max_stack.
278 switch (opcode.StackBehaviourPop) {
279 case StackBehaviour.Varpop:
280 break; /* we are conservative and assume it doesn't decrease the stack needs */
281 case StackBehaviour.Pop1:
282 case StackBehaviour.Popi:
283 case StackBehaviour.Popref:
286 case StackBehaviour.Pop1_pop1:
287 case StackBehaviour.Popi_pop1:
288 case StackBehaviour.Popi_popi:
289 case StackBehaviour.Popi_popi8:
290 case StackBehaviour.Popi_popr4:
291 case StackBehaviour.Popi_popr8:
292 case StackBehaviour.Popref_pop1:
293 case StackBehaviour.Popref_popi:
296 case StackBehaviour.Popi_popi_popi:
297 case StackBehaviour.Popref_popi_popi:
298 case StackBehaviour.Popref_popi_popi8:
299 case StackBehaviour.Popref_popi_popr4:
300 case StackBehaviour.Popref_popi_popr8:
301 case StackBehaviour.Popref_popi_popref:
307 private static int target_len (OpCode opcode)
309 if (opcode.OperandType == OperandType.InlineBrTarget)
314 private void InternalEndClause ()
316 switch (ex_handlers [cur_block].LastClauseType ()) {
317 case ILExceptionBlock.CATCH:
318 // how could we optimize code size here?
319 Emit (OpCodes.Leave, ex_handlers [cur_block].end);
321 case ILExceptionBlock.FAULT:
322 case ILExceptionBlock.FINALLY:
323 Emit (OpCodes.Endfinally);
325 case ILExceptionBlock.FILTER:
326 Emit (OpCodes.Endfilter);
331 public virtual void BeginCatchBlock (Type exceptionType)
333 if (open_blocks.Count <= 0)
334 throw new NotSupportedException ("Not in an exception block");
335 InternalEndClause ();
336 ex_handlers [cur_block].AddCatch (exceptionType, code_len);
337 cur_stack = 1; // the exception object is on the stack by default
338 if (max_stack < cur_stack)
339 max_stack = cur_stack;
340 //System.Console.WriteLine ("Begin catch Block: {0} {1}",exceptionType.ToString(), max_stack);
341 //throw new NotImplementedException ();
345 public virtual void BeginExceptFilterBlock ()
347 throw new NotImplementedException ();
350 public virtual Label BeginExceptionBlock ()
352 //System.Console.WriteLine ("Begin Block");
354 if (ex_handlers != null) {
355 cur_block = ex_handlers.Length;
356 ILExceptionInfo[] new_ex = new ILExceptionInfo [cur_block + 1];
357 System.Array.Copy (ex_handlers, new_ex, cur_block);
358 ex_handlers = new_ex;
360 ex_handlers = new ILExceptionInfo [1];
363 open_blocks.Push (cur_block);
364 ex_handlers [cur_block].start = code_len;
365 return ex_handlers [cur_block].end = DefineLabel ();
368 public virtual void BeginFaultBlock()
370 if (open_blocks.Count <= 0)
371 throw new NotSupportedException ("Not in an exception block");
372 //System.Console.WriteLine ("Begin fault Block");
373 //throw new NotImplementedException ();
376 public virtual void BeginFinallyBlock()
378 if (open_blocks.Count <= 0)
379 throw new NotSupportedException ("Not in an exception block");
380 InternalEndClause ();
381 //System.Console.WriteLine ("Begin finally Block");
382 ex_handlers [cur_block].AddFinally (code_len);
385 public virtual void BeginScope ()
387 if (sym_writer != null) {
389 scopes = new Stack ();
390 scopes.Push (sym_writer.OpenScope (code_len));
394 public LocalBuilder DeclareLocal (Type localType)
396 LocalBuilder res = new LocalBuilder (localType, this);
397 if (locals != null) {
398 LocalBuilder[] new_l = new LocalBuilder [locals.Length + 1];
399 System.Array.Copy (locals, new_l, locals.Length);
400 new_l [locals.Length] = res;
403 locals = new LocalBuilder [1];
406 res.position = (ushort)(locals.Length - 1);
410 public virtual Label DefineLabel ()
413 labels = new LabelData [defaultLabelsSize];
414 else if (num_labels >= labels.Length) {
415 LabelData [] t = new LabelData [labels.Length * 2];
416 Array.Copy (labels, t, labels.Length);
420 labels [num_labels] = new LabelData (-1, 0);
422 return new Label (num_labels++);
425 public virtual void Emit (OpCode opcode)
431 public virtual void Emit (OpCode opcode, Byte val)
435 code [code_len++] = val;
438 public virtual void Emit (OpCode opcode, ConstructorInfo constructor)
440 int token = token_gen.GetToken (constructor);
443 if (constructor.DeclaringType.Module == module)
444 add_token_fixup (constructor);
447 if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
448 cur_stack -= constructor.GetParameterCount ();
451 public virtual void Emit (OpCode opcode, double val)
453 Double.AssertEndianity (out val);
455 byte[] s = System.BitConverter.GetBytes (val);
458 if (BitConverter.IsLittleEndian){
459 System.Array.Copy (s, 0, code, code_len, 8);
462 code [code_len++] = s [7];
463 code [code_len++] = s [6];
464 code [code_len++] = s [5];
465 code [code_len++] = s [4];
466 code [code_len++] = s [3];
467 code [code_len++] = s [2];
468 code [code_len++] = s [1];
469 code [code_len++] = s [0];
473 public virtual void Emit (OpCode opcode, FieldInfo field)
475 int token = token_gen.GetToken (field);
478 if (field.DeclaringType.Module == module)
479 add_token_fixup (field);
483 public virtual void Emit (OpCode opcode, Int16 val)
487 code [code_len++] = (byte) (val & 0xFF);
488 code [code_len++] = (byte) ((val >> 8) & 0xFF);
491 public virtual void Emit (OpCode opcode, int val)
498 public virtual void Emit (OpCode opcode, long val)
502 code [code_len++] = (byte) (val & 0xFF);
503 code [code_len++] = (byte) ((val >> 8) & 0xFF);
504 code [code_len++] = (byte) ((val >> 16) & 0xFF);
505 code [code_len++] = (byte) ((val >> 24) & 0xFF);
506 code [code_len++] = (byte) ((val >> 32) & 0xFF);
507 code [code_len++] = (byte) ((val >> 40) & 0xFF);
508 code [code_len++] = (byte) ((val >> 48) & 0xFF);
509 code [code_len++] = (byte) ((val >> 56) & 0xFF);
512 public virtual void Emit (OpCode opcode, Label label)
514 int tlen = target_len (opcode);
517 if (cur_stack > labels [label.label].maxStack)
518 labels [label.label].maxStack = cur_stack;
521 fixups = new LabelFixup [defaultFixupSize];
522 else if (num_fixups >= fixups.Length) {
523 LabelFixup[] newf = new LabelFixup [fixups.Length + 16];
524 System.Array.Copy (fixups, newf, fixups.Length);
527 fixups [num_fixups].offset = tlen;
528 fixups [num_fixups].pos = code_len;
529 fixups [num_fixups].label_idx = label.label;
535 public virtual void Emit (OpCode opcode, Label[] labels)
537 /* opcode needs to be switch. */
538 int count = labels.Length;
539 make_room (6 + count * 4);
542 for (int i = 0; i < count; ++i)
543 if (cur_stack > this.labels [labels [i].label].maxStack)
544 this.labels [labels [i].label].maxStack = cur_stack;
548 fixups = new LabelFixup [defaultFixupSize + count];
549 else if (num_fixups + count >= fixups.Length) {
550 LabelFixup[] newf = new LabelFixup [fixups.Length + count + 16];
551 System.Array.Copy (fixups, newf, fixups.Length);
555 // ECMA 335, Partition III, p94 (7-10)
557 // The switch instruction implements a jump table. The format of
558 // the instruction is an unsigned int32 representing the number of targets N,
559 // followed by N int32 values specifying jump targets: these targets are
560 // represented as offsets (positive or negative) from the beginning of the
561 // instruction following this switch instruction.
563 // We must make sure it gets an offset from the *end* of the last label
564 // (eg, the beginning of the instruction following this).
566 // remaining is the number of bytes from the current instruction to the
567 // instruction that will be emitted.
569 for (int i = 0, remaining = count * 4; i < count; ++i, remaining -= 4) {
570 fixups [num_fixups].offset = remaining;
571 fixups [num_fixups].pos = code_len;
572 fixups [num_fixups].label_idx = labels [i].label;
578 public virtual void Emit (OpCode opcode, LocalBuilder lbuilder)
580 uint pos = lbuilder.position;
581 bool load_addr = false;
582 bool is_store = false;
585 if (lbuilder.ilgen != this)
586 throw new Exception ("Trying to emit a local from a different ILGenerator.");
588 /* inline the code from ll_emit () to optimize il code size */
589 if (opcode.StackBehaviourPop == StackBehaviour.Pop1) {
594 if (cur_stack > max_stack)
595 max_stack = cur_stack;
596 load_addr = opcode.StackBehaviourPush == StackBehaviour.Pushi;
600 code [code_len++] = (byte)0x12;
601 code [code_len++] = (byte)pos;
603 code [code_len++] = (byte)0xfe;
604 code [code_len++] = (byte)0x0d;
605 code [code_len++] = (byte)(pos & 0xff);
606 code [code_len++] = (byte)((pos >> 8) & 0xff);
611 code [code_len++] = (byte)(0x0a + pos);
612 } else if (pos < 256) {
613 code [code_len++] = (byte)0x13;
614 code [code_len++] = (byte)pos;
616 code [code_len++] = (byte)0xfe;
617 code [code_len++] = (byte)0x0e;
618 code [code_len++] = (byte)(pos & 0xff);
619 code [code_len++] = (byte)((pos >> 8) & 0xff);
623 code [code_len++] = (byte)(0x06 + pos);
624 } else if (pos < 256) {
625 code [code_len++] = (byte)0x11;
626 code [code_len++] = (byte)pos;
628 code [code_len++] = (byte)0xfe;
629 code [code_len++] = (byte)0x0c;
630 code [code_len++] = (byte)(pos & 0xff);
631 code [code_len++] = (byte)((pos >> 8) & 0xff);
637 public virtual void Emit (OpCode opcode, MethodInfo method)
640 throw new ArgumentNullException ("method");
642 int token = token_gen.GetToken (method);
645 if (method.DeclaringType.Module == module)
646 add_token_fixup (method);
648 if (method.ReturnType != void_type)
651 if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
652 cur_stack -= method.GetParameterCount ();
655 private void Emit (OpCode opcode, MethodInfo method, int token)
659 if (method.DeclaringType.Module == module)
660 add_token_fixup (method);
662 if (method.ReturnType != void_type)
665 if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
666 cur_stack -= method.GetParameterCount ();
669 [CLSCompliant(false)]
670 public void Emit (OpCode opcode, sbyte val)
674 code [code_len++] = (byte)val;
677 public virtual void Emit (OpCode opcode, SignatureHelper shelper)
679 int token = token_gen.GetToken (shelper);
685 public virtual void Emit (OpCode opcode, float val)
687 byte[] s = System.BitConverter.GetBytes (val);
690 if (BitConverter.IsLittleEndian){
691 System.Array.Copy (s, 0, code, code_len, 4);
694 code [code_len++] = s [3];
695 code [code_len++] = s [2];
696 code [code_len++] = s [1];
697 code [code_len++] = s [0];
701 public virtual void Emit (OpCode opcode, string val)
703 int token = token_gen.GetToken (val);
709 public virtual void Emit (OpCode opcode, Type type)
713 emit_int (token_gen.GetToken (type));
716 [MonoTODO ("Do something about varargs method")]
717 public void EmitCall (OpCode opcode, MethodInfo methodinfo, Type[] optionalParamTypes)
719 if (methodinfo == null)
720 throw new ArgumentNullException ("methodinfo can not be null");
721 short value = opcode.Value;
722 if (!(value == OpCodes.Call.Value || value == OpCodes.Callvirt.Value))
723 throw new NotSupportedException ("Only Call and CallVirt are allowed");
724 if (optionalParamTypes != null){
725 if ((methodinfo.CallingConvention & CallingConventions.VarArgs) == 0){
726 throw new InvalidOperationException ("Method is not VarArgs method and optional types were passed");
729 int token = token_gen.GetToken (methodinfo, optionalParamTypes);
730 Emit (opcode, methodinfo, token);
733 Emit (opcode, methodinfo);
736 public void EmitCalli (OpCode opcode, CallingConvention unmanagedCallConv, Type returnType, Type[] paramTypes)
738 SignatureHelper helper
739 = SignatureHelper.GetMethodSigHelper (module, 0, unmanagedCallConv, returnType, paramTypes);
740 Emit (opcode, helper);
743 public void EmitCalli (OpCode opcode, CallingConventions callConv, Type returnType, Type[] paramTypes, Type[] optionalParamTypes)
745 if (optionalParamTypes != null)
746 throw new NotImplementedException ();
748 SignatureHelper helper
749 = SignatureHelper.GetMethodSigHelper (module, callConv, 0, returnType, paramTypes);
750 Emit (opcode, helper);
753 public virtual void EmitWriteLine (FieldInfo field)
756 throw new ArgumentNullException ("field");
758 // The MS implementation does not check for valuetypes here but it
759 // should. Also, it should check that if the field is not static,
760 // then it is a member of this type.
762 Emit (OpCodes.Ldsfld, field);
764 Emit (OpCodes.Ldarg_0);
765 Emit (OpCodes.Ldfld, field);
768 typeof (Console).GetMethod ("WriteLine",
769 new Type[1] { field.FieldType }));
772 public virtual void EmitWriteLine (LocalBuilder lbuilder)
774 if (lbuilder == null)
775 throw new ArgumentNullException ("lbuilder");
776 if (lbuilder.LocalType is TypeBuilder)
777 throw new ArgumentException ("Output streams do not support TypeBuilders.");
778 // The MS implementation does not check for valuetypes here but it
780 Emit (OpCodes.Ldloc, lbuilder);
782 typeof (Console).GetMethod ("WriteLine",
783 new Type[1] { lbuilder.LocalType }));
786 public virtual void EmitWriteLine (string val)
788 Emit (OpCodes.Ldstr, val);
790 typeof (Console).GetMethod ("WriteLine",
791 new Type[1] { typeof(string)}));
794 public virtual void EndExceptionBlock ()
796 if (open_blocks.Count <= 0)
797 throw new NotSupportedException ("Not in an exception block");
798 InternalEndClause ();
799 MarkLabel (ex_handlers [cur_block].end);
800 ex_handlers [cur_block].End (code_len);
801 ex_handlers [cur_block].Debug (cur_block);
802 //System.Console.WriteLine ("End Block {0} (handlers: {1})", cur_block, ex_handlers [cur_block].NumHandlers ());
804 if (open_blocks.Count > 0)
805 cur_block = (int)open_blocks.Peek ();
806 //Console.WriteLine ("curblock restored to {0}", cur_block);
807 //throw new NotImplementedException ();
810 public virtual void EndScope ()
812 if (sym_writer != null) {
813 sym_writer.CloseScope (code_len);
815 throw new InvalidOperationException ();
820 public virtual void MarkLabel (Label loc)
822 if (loc.label < 0 || loc.label >= num_labels)
823 throw new System.ArgumentException ("The label is not valid");
824 if (labels [loc.label].addr >= 0)
825 throw new System.ArgumentException ("The label was already defined");
826 labels [loc.label].addr = code_len;
827 if (labels [loc.label].maxStack > cur_stack)
828 cur_stack = labels [loc.label].maxStack;
831 public virtual void MarkSequencePoint (ISymbolDocumentWriter document, int startLine,
832 int startColumn, int endLine, int endColumn)
834 if (sym_writer == null)
837 sym_writer.MarkSequencePoint (code_len, startLine, startColumn);
840 public virtual void ThrowException (Type exceptionType)
842 if (exceptionType == null)
843 throw new ArgumentNullException ("exceptionType");
844 if (! ((exceptionType == typeof (Exception)) ||
845 exceptionType.IsSubclassOf (typeof (Exception))))
846 throw new ArgumentException ("Type should be an exception type", "exceptionType");
847 ConstructorInfo ctor = exceptionType.GetConstructor (new Type[0]);
849 throw new ArgumentException ("Type should have a default constructor", "exceptionType");
850 Emit (OpCodes.Newobj, ctor);
851 Emit (OpCodes.Throw);
855 public void UsingNamespace (String usingNamespace)
857 throw new NotImplementedException ();
860 internal void label_fixup ()
862 for (int i = 0; i < num_fixups; ++i) {
864 // Diff is the offset from the end of the jump instruction to the address of the label
865 int diff = labels [fixups [i].label_idx].addr - (fixups [i].pos + fixups [i].offset);
866 if (fixups [i].offset == 1) {
867 code [fixups [i].pos] = (byte)((sbyte) diff);
869 int old_cl = code_len;
870 code_len = fixups [i].pos;