2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / corlib / System.Reflection.Emit / ILGenerator.cs
index 95771f95504735c30745ed26759585ecba07e50a..b99186888fde6e57f31ef2f1b953d679ecf54171 100644 (file)
@@ -1,4 +1,27 @@
 
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 //
 // System.Reflection.Emit/ILGenerator.cs
 //
@@ -12,7 +35,6 @@ using System;
 using System.Collections;
 using System.Diagnostics.SymbolStore;
 using System.Runtime.InteropServices;
-using Mono.CSharp.Debugger;
 
 namespace System.Reflection.Emit {
 
@@ -71,6 +93,17 @@ namespace System.Reflection.Emit {
                        handlers [i].extype = null;
                }
 
+               internal void AddFault (int offset)
+               {
+                       int i;
+                       End (offset);
+                       add_block (offset);
+                       i = handlers.Length - 1;
+                       handlers [i].type = ILExceptionBlock.FAULT;
+                       handlers [i].start = offset;
+                       handlers [i].extype = null;
+               }
+
                internal void End (int offset)
                {
                        if (handlers == null)
@@ -117,16 +150,38 @@ namespace System.Reflection.Emit {
                public int code_pos;
        }
 
+       internal interface TokenGenerator {
+               int GetToken (string str);
+
+               int GetToken (MemberInfo member);
+
+               int GetToken (MethodInfo method, Type[] opt_param_types);
+
+               int GetToken (SignatureHelper helper);
+       }               
+
        public class ILGenerator: Object {
                private struct LabelFixup {
-                       public int size;
-                       public int pos;                 // the location of the fixup
-                       public int label_base;  // the base address for this fixup
-                       public int label_idx;
+                       public int offset;    // The number of bytes between pos and the
+                                                             // offset of the jump
+                       public int pos;       // Where offset of the label is placed
+                       public int label_idx; // The label to jump to
                };
-               static Type void_type = typeof (void);
+               
+               struct LabelData {
+                       public LabelData (int addr, int maxStack)
+                       {
+                               this.addr = addr;
+                               this.maxStack = maxStack;
+                       }
+                       
+                       public int addr;
+                       public int maxStack; 
+               }
+               
+               static readonly Type void_type = typeof (void);
+               #region Sync with reflection.h
                private byte[] code;
-               private MethodBase mbuilder; /* a MethodBuilder or ConstructorBuilder */
                private int code_len;
                private int max_stack;
                private int cur_stack;
@@ -134,40 +189,34 @@ namespace System.Reflection.Emit {
                private ILExceptionInfo[] ex_handlers;
                private int num_token_fixups;
                private ILTokenInfo[] token_fixups;
-               private int[] label_to_addr;
-               private int[] label_to_max_stack;
+               #endregion
+               
+               private LabelData [] labels;
                private int num_labels;
                private LabelFixup[] fixups;
                private int num_fixups;
-               private ModuleBuilder module;
-               private AssemblyBuilder abuilder;
-               private IMonoSymbolWriter sym_writer;
+               internal Module module;
                private Stack scopes;
                private int cur_block;
                private Stack open_blocks;
+               private TokenGenerator token_gen;
+               
+               const int defaultFixupSize = 8;
+               const int defaultLabelsSize = 8;
 
-               internal ILGenerator (MethodBase mb, int size)
+               internal ILGenerator (Module m, TokenGenerator token_gen, int size)
                {
                        if (size < 0)
                                size = 128;
                        code_len = 0;
                        code = new byte [size];
-                       mbuilder = mb;
                        cur_stack = max_stack = 0;
                        num_fixups = num_labels = 0;
-                       label_to_addr = new int [8];
-                       label_to_max_stack = new int [8];
-                       fixups = new LabelFixup [8];
                        token_fixups = new ILTokenInfo [8];
                        num_token_fixups = 0;
-                       if (mb is MethodBuilder) {
-                               module = (ModuleBuilder)((MethodBuilder)mb).TypeBuilder.Module;
-                       } else if (mb is ConstructorBuilder) {
-                               module = (ModuleBuilder)((ConstructorBuilder)mb).TypeBuilder.Module;
-                       }
-                       abuilder = (AssemblyBuilder)module.Assembly;
-                       sym_writer = module.symbol_writer;
+                       module = m;
                        open_blocks = new Stack ();
+                       this.token_gen = token_gen;
                }
 
                private void add_token_fixup (MemberInfo mi)
@@ -328,7 +377,7 @@ namespace System.Reflection.Emit {
                        if (open_blocks.Count <= 0)
                                throw new NotSupportedException ("Not in an exception block");
                        //System.Console.WriteLine ("Begin fault Block");
-                       //throw new NotImplementedException ();
+                       ex_handlers [cur_block].AddFault (code_len);
                }
                
                public virtual void BeginFinallyBlock()
@@ -341,17 +390,24 @@ namespace System.Reflection.Emit {
                }
                
                public virtual void BeginScope ()
-               {
-                       if (sym_writer != null) {
-                               if (scopes == null)
-                                       scopes = new Stack ();
-                               scopes.Push (sym_writer.OpenScope (code_len));
-                       }
-               }
+               { }
                
                public LocalBuilder DeclareLocal (Type localType)
                {
-                       LocalBuilder res = new LocalBuilder (module, localType, this);
+                       return DeclareLocal (localType, false);
+               }
+
+
+#if NET_2_0
+               public
+#else
+               internal
+#endif
+               LocalBuilder DeclareLocal (Type localType, bool pinned)
+               {
+                       LocalBuilder res = new LocalBuilder (localType, this);
+                       res.is_pinned = pinned;
+                       
                        if (locals != null) {
                                LocalBuilder[] new_l = new LocalBuilder [locals.Length + 1];
                                System.Array.Copy (locals, new_l, locals.Length);
@@ -361,22 +417,22 @@ namespace System.Reflection.Emit {
                                locals = new LocalBuilder [1];
                                locals [0] = res;
                        }
-                       res.position = (uint)(locals.Length - 1);
+                       res.position = (ushort)(locals.Length - 1);
                        return res;
                }
                
                public virtual Label DefineLabel ()
                {
-                       if (num_labels >= label_to_addr.Length) {
-                               int[] new_l = new int [label_to_addr.Length * 2];
-                               System.Array.Copy (label_to_addr, new_l, label_to_addr.Length);
-                               int[] new_s = new int [label_to_addr.Length * 2];
-                               System.Array.Copy (label_to_max_stack, new_s, label_to_addr.Length);
-                               label_to_addr = new_l;
-                               label_to_max_stack = new_s;
+                       if (labels == null)
+                               labels = new LabelData [defaultLabelsSize];
+                       else if (num_labels >= labels.Length) {
+                               LabelData [] t = new LabelData [labels.Length * 2];
+                               Array.Copy (labels, t, labels.Length);
+                               labels = t;
                        }
-                       label_to_addr [num_labels] = -1;
-                       label_to_max_stack [num_labels] = 0;
+                       
+                       labels [num_labels] = new LabelData (-1, 0);
+                       
                        return new Label (num_labels++);
                }
                
@@ -395,15 +451,15 @@ namespace System.Reflection.Emit {
                
                public virtual void Emit (OpCode opcode, ConstructorInfo constructor)
                {
-                       int token = abuilder.GetToken (constructor);
+                       int token = token_gen.GetToken (constructor);
                        make_room (6);
                        ll_emit (opcode);
                        if (constructor.DeclaringType.Module == module)
                                add_token_fixup (constructor);
                        emit_int (token);
-                       ParameterInfo[] mparams = constructor.GetParameters();
-                       if ((opcode.StackBehaviourPop == StackBehaviour.Varpop) && (mparams != null))
-                               cur_stack -= mparams.Length;
+                       
+                       if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
+                               cur_stack -= constructor.GetParameterCount ();
                }
                
                public virtual void Emit (OpCode opcode, double val)
@@ -430,7 +486,7 @@ namespace System.Reflection.Emit {
                
                public virtual void Emit (OpCode opcode, FieldInfo field)
                {
-                       int token = abuilder.GetToken (field);
+                       int token = token_gen.GetToken (field);
                        make_room (6);
                        ll_emit (opcode);
                        if (field.DeclaringType.Module == module)
@@ -472,16 +528,18 @@ namespace System.Reflection.Emit {
                        int tlen = target_len (opcode);
                        make_room (6);
                        ll_emit (opcode);
-                       if (cur_stack > label_to_max_stack [label.label])
-                               label_to_max_stack [label.label] = cur_stack;
-                       if (num_fixups >= fixups.Length) {
+                       if (cur_stack > labels [label.label].maxStack)
+                               labels [label.label].maxStack = cur_stack;
+                       
+                       if (fixups == null)
+                               fixups = new LabelFixup [defaultFixupSize]; 
+                       else if (num_fixups >= fixups.Length) {
                                LabelFixup[] newf = new LabelFixup [fixups.Length + 16];
                                System.Array.Copy (fixups, newf, fixups.Length);
                                fixups = newf;
                        }
-                       fixups [num_fixups].size = tlen;
+                       fixups [num_fixups].offset = tlen;
                        fixups [num_fixups].pos = code_len;
-                       fixups [num_fixups].label_base = code_len;
                        fixups [num_fixups].label_idx = label.label;
                        num_fixups++;
                        code_len += tlen;
@@ -496,20 +554,35 @@ namespace System.Reflection.Emit {
                        ll_emit (opcode);
 
                        for (int i = 0; i < count; ++i)
-                               if (cur_stack > label_to_max_stack [labels [i].label])
-                                       label_to_max_stack [labels [i].label] = cur_stack;
+                               if (cur_stack > this.labels [labels [i].label].maxStack)
+                                       this.labels [labels [i].label].maxStack = cur_stack;
 
-                       int switch_base = code_len + count*4;
                        emit_int (count);
-                       if (num_fixups + count >= fixups.Length) {
+                       if (fixups == null)
+                               fixups = new LabelFixup [defaultFixupSize + count]; 
+                       else if (num_fixups + count >= fixups.Length) {
                                LabelFixup[] newf = new LabelFixup [fixups.Length + count + 16];
                                System.Array.Copy (fixups, newf, fixups.Length);
                                fixups = newf;
                        }
-                       for (int i = 0; i < count; ++i) {
-                               fixups [num_fixups].size = 4;
+                       
+                       // ECMA 335, Partition III, p94 (7-10)
+                       //
+                       // The switch instruction implements a jump table. The format of 
+                       // the instruction is an unsigned int32 representing the number of targets N,
+                       // followed by N int32 values specifying jump targets: these targets are
+                       // represented as offsets (positive or negative) from the beginning of the 
+                       // instruction following this switch instruction.
+                       //
+                       // We must make sure it gets an offset from the *end* of the last label
+                       // (eg, the beginning of the instruction following this).
+                       //
+                       // remaining is the number of bytes from the current instruction to the
+                       // instruction that will be emitted.
+                       
+                       for (int i = 0, remaining = count * 4; i < count; ++i, remaining -= 4) {
+                               fixups [num_fixups].offset = remaining;
                                fixups [num_fixups].pos = code_len;
-                               fixups [num_fixups].label_base = switch_base;
                                fixups [num_fixups].label_idx = labels [i].label;
                                num_fixups++;
                                code_len += 4;
@@ -580,7 +653,7 @@ namespace System.Reflection.Emit {
                        if (method == null)
                                throw new ArgumentNullException ("method");
 
-                       int token = abuilder.GetToken (method);
+                       int token = token_gen.GetToken (method);
                        make_room (6);
                        ll_emit (opcode);
                        if (method.DeclaringType.Module == module)
@@ -588,9 +661,23 @@ namespace System.Reflection.Emit {
                        emit_int (token);
                        if (method.ReturnType != void_type)
                                cur_stack ++;
-                       ParameterInfo[] mparams = method.GetParameters();
-                       if ((opcode.StackBehaviourPop == StackBehaviour.Varpop) && (mparams != null))
-                               cur_stack -= mparams.Length;
+
+                       if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
+                               cur_stack -= method.GetParameterCount ();
+               }
+
+               private void Emit (OpCode opcode, MethodInfo method, int token)
+               {
+                       make_room (6);
+                       ll_emit (opcode);
+                       if (method.DeclaringType.Module == module)
+                               add_token_fixup (method);
+                       emit_int (token);
+                       if (method.ReturnType != void_type)
+                               cur_stack ++;
+
+                       if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
+                               cur_stack -= method.GetParameterCount ();
                }
 
                [CLSCompliant(false)]
@@ -603,7 +690,7 @@ namespace System.Reflection.Emit {
 
                public virtual void Emit (OpCode opcode, SignatureHelper shelper)
                {
-                       int token = abuilder.GetToken (shelper);
+                       int token = token_gen.GetToken (shelper);
                        make_room (6);
                        ll_emit (opcode);
                        emit_int (token);
@@ -627,7 +714,7 @@ namespace System.Reflection.Emit {
 
                public virtual void Emit (OpCode opcode, string val)
                {
-                       int token = abuilder.GetToken (val);
+                       int token = token_gen.GetToken (val);
                        make_room (6);
                        ll_emit (opcode);
                        emit_int (token);
@@ -637,7 +724,7 @@ namespace System.Reflection.Emit {
                {
                        make_room (6);
                        ll_emit (opcode);
-                       emit_int (abuilder.GetToken (type));
+                       emit_int (token_gen.GetToken (type));
                }
 
                [MonoTODO ("Do something about varargs method")]
@@ -652,6 +739,10 @@ namespace System.Reflection.Emit {
                                if ((methodinfo.CallingConvention & CallingConventions.VarArgs)  == 0){
                                        throw new InvalidOperationException ("Method is not VarArgs method and optional types were passed");
                                }
+
+                               int token = token_gen.GetToken (methodinfo, optionalParamTypes);
+                               Emit (opcode, methodinfo, token);
+                               return;
                        }
                        Emit (opcode, methodinfo);
                }
@@ -688,8 +779,8 @@ namespace System.Reflection.Emit {
                                Emit (OpCodes.Ldfld, field);
                        }
                        Emit (OpCodes.Call, 
-                                 typeof (Console).GetMethod ("WriteLine",
-                                                                                         new Type[1] { field.FieldType }));
+                             typeof (Console).GetMethod ("WriteLine",
+                                                         new Type[1] { field.FieldType }));
                }
 
                public virtual void EmitWriteLine (LocalBuilder lbuilder)
@@ -702,16 +793,16 @@ namespace System.Reflection.Emit {
                        // should.
                        Emit (OpCodes.Ldloc, lbuilder);
                        Emit (OpCodes.Call, 
-                                 typeof (Console).GetMethod ("WriteLine",
-                                                                                         new Type[1] { lbuilder.LocalType }));
+                             typeof (Console).GetMethod ("WriteLine",
+                                                         new Type[1] { lbuilder.LocalType }));
                }
                
                public virtual void EmitWriteLine (string val)
                {
                        Emit (OpCodes.Ldstr, val);
                        Emit (OpCodes.Call, 
-                                 typeof (Console).GetMethod ("WriteLine",
-                                                                                         new Type[1] { typeof(string)}));
+                             typeof (Console).GetMethod ("WriteLine",
+                                                         new Type[1] { typeof(string)}));
                }
 
                public virtual void EndExceptionBlock ()
@@ -731,34 +822,22 @@ namespace System.Reflection.Emit {
                }
 
                public virtual void EndScope ()
-               {
-                       if (sym_writer != null) {
-                               sym_writer.CloseScope (code_len);
-                               if (scopes == null)
-                                       throw new InvalidOperationException ();
-                               scopes.Pop ();
-                       }
-               }
+               { }
 
                public virtual void MarkLabel (Label loc)
                {
                        if (loc.label < 0 || loc.label >= num_labels)
                                throw new System.ArgumentException ("The label is not valid");
-                       if (label_to_addr [loc.label] >= 0)
+                       if (labels [loc.label].addr >= 0)
                                throw new System.ArgumentException ("The label was already defined");
-                       label_to_addr [loc.label] = code_len;
-                       if (label_to_max_stack [loc.label] > cur_stack)
-                               cur_stack = label_to_max_stack [loc.label];
+                       labels [loc.label].addr = code_len;
+                       if (labels [loc.label].maxStack > cur_stack)
+                               cur_stack = labels [loc.label].maxStack;
                }
 
                public virtual void MarkSequencePoint (ISymbolDocumentWriter document, int startLine,
                                                       int startColumn, int endLine, int endColumn)
-               {
-                       if (sym_writer == null)
-                               return;
-
-                       sym_writer.MarkSequencePoint (code_len, startLine, startColumn);
-               }
+               { }
 
                public virtual void ThrowException (Type exceptionType)
                {
@@ -782,18 +861,24 @@ namespace System.Reflection.Emit {
 
                internal void label_fixup ()
                {
-                       int i;
-                       for (i = 0; i < num_fixups; ++i) {
-                               int diff = label_to_addr [fixups [i].label_idx] - fixups [i].label_base;
-                               if (fixups [i].size == 1) {
-                                       code [fixups [i].pos] = (byte)((sbyte) diff - 1);
+                       for (int i = 0; i < num_fixups; ++i) {
+                               
+                               // Diff is the offset from the end of the jump instruction to the address of the label
+                               int diff = labels [fixups [i].label_idx].addr - (fixups [i].pos + fixups [i].offset);
+                               if (fixups [i].offset == 1) {
+                                       code [fixups [i].pos] = (byte)((sbyte) diff);
                                } else {
                                        int old_cl = code_len;
                                        code_len = fixups [i].pos;
-                                       emit_int (diff - 4);
+                                       emit_int (diff);
                                        code_len = old_cl;
                                }
                        }
                }
+
+               internal static int Mono_GetCurrentOffset (ILGenerator ig)
+               {
+                       return ig.code_len;
+               }
        }
 }