New test.
[mono.git] / mcs / class / corlib / System.Reflection.Emit / ILGenerator.cs
index 3d64eb2b59e93d4f6499a4276e88da43e2d40367..ffea0a721a682c81812555e02f43ac486534d4b5 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 {
 
@@ -34,7 +56,7 @@ namespace System.Reflection.Emit {
                        if (extype != null)
                                System.Console.WriteLine (" extype="+extype.ToString());
                        else
-                               System.Console.WriteLine ("");
+                               System.Console.WriteLine (String.Empty);
 #endif
                }
        }
@@ -71,6 +93,28 @@ 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 AddFilter (int offset)
+               {
+                       int i;
+                       End (offset);
+                       add_block (offset);
+                       i = handlers.Length - 1;
+                       handlers [i].type = ILExceptionBlock.FILTER;
+                       handlers [i].extype = null;
+                       handlers [i].filter_offset = offset;
+               }
+
                internal void End (int offset)
                {
                        if (handlers == null)
@@ -88,6 +132,12 @@ namespace System.Reflection.Emit {
                                return ILExceptionBlock.CATCH;
                }
 
+               internal void PatchLastClauseStart (int start)
+               {
+                       if (handlers != null && handlers.Length > 0)
+                               handlers [handlers.Length - 1].start = start;
+               }
+
                internal void Debug (int b)
                {
 #if NO
@@ -127,7 +177,11 @@ namespace System.Reflection.Emit {
                int GetToken (SignatureHelper helper);
        }               
 
-       public class ILGenerator: Object {
+#if NET_2_0
+       [ComVisible (true)]
+#endif
+       [ClassInterface (ClassInterfaceType.None)]
+       public class ILGenerator: _ILGenerator {
                private struct LabelFixup {
                        public int offset;    // The number of bytes between pos and the
                                                              // offset of the jump
@@ -163,32 +217,28 @@ namespace System.Reflection.Emit {
                private LabelFixup[] fixups;
                private int num_fixups;
                internal Module module;
-               internal IMonoSymbolWriter sym_writer;
                private Stack scopes;
                private int cur_block;
                private Stack open_blocks;
                private TokenGenerator token_gen;
                
-               const int defaultFixupSize = 8;
-               const int defaultLabelsSize = 8;
+               const int defaultFixupSize = 4;
+               const int defaultLabelsSize = 4;
+               const int defaultExceptionStackSize = 2;
+               
+               ArrayList sequencePointLists;
+               SequencePointList currentSequence;
 
                internal ILGenerator (Module m, TokenGenerator token_gen, int size)
                {
                        if (size < 0)
                                size = 128;
-                       code_len = 0;
                        code = new byte [size];
-                       cur_stack = max_stack = 0;
-                       num_fixups = num_labels = 0;
                        token_fixups = new ILTokenInfo [8];
-                       num_token_fixups = 0;
                        module = m;
-                       if (module is ModuleBuilder)
-                               sym_writer = ((ModuleBuilder)module).symbol_writer;
-                       open_blocks = new Stack ();
                        this.token_gen = token_gen;
                }
-
+               
                private void add_token_fixup (MemberInfo mi)
                {
                        if (num_token_fixups == token_fixups.Length) {
@@ -292,6 +342,7 @@ namespace System.Reflection.Emit {
                {
                        switch (ex_handlers [cur_block].LastClauseType ()) {
                        case ILExceptionBlock.CATCH:
+                       case ILExceptionBlock.FILTER:
                                // how could we optimize code size here?
                                Emit (OpCodes.Leave, ex_handlers [cur_block].end);
                                break;
@@ -299,34 +350,51 @@ namespace System.Reflection.Emit {
                        case ILExceptionBlock.FINALLY:
                                Emit (OpCodes.Endfinally);
                                break;
-                       case ILExceptionBlock.FILTER:
-                               Emit (OpCodes.Endfilter);
-                               break;
                        }
                }
 
                public virtual void BeginCatchBlock (Type exceptionType)
                {
+                       if (open_blocks == null)
+                               open_blocks = new Stack (defaultExceptionStackSize);
+
                        if (open_blocks.Count <= 0)
                                throw new NotSupportedException ("Not in an exception block");
-                       InternalEndClause ();
-                       ex_handlers [cur_block].AddCatch (exceptionType, code_len);
+
+                       if (ex_handlers [cur_block].LastClauseType () == ILExceptionBlock.FILTER) {
+                               if (exceptionType != null)
+                                       throw new ArgumentException ("Do not supply an exception type for filter clause");
+                               Emit (OpCodes.Endfilter);
+                               ex_handlers [cur_block].PatchLastClauseStart (code_len);
+                       } else {
+                               InternalEndClause ();
+                               ex_handlers [cur_block].AddCatch (exceptionType, code_len);
+                       }
+                       
                        cur_stack = 1; // the exception object is on the stack by default
                        if (max_stack < cur_stack)
                                max_stack = cur_stack;
+
                        //System.Console.WriteLine ("Begin catch Block: {0} {1}",exceptionType.ToString(), max_stack);
-                       //throw new NotImplementedException ();
                }
 
-               [MonoTODO]
                public virtual void BeginExceptFilterBlock ()
                {
-                       throw new NotImplementedException ();
+                       if (open_blocks == null)
+                               open_blocks = new Stack (defaultExceptionStackSize);
+                       
+                       if (open_blocks.Count <= 0)
+                               throw new NotSupportedException ("Not in an exception block");
+                       InternalEndClause ();
+
+                       ex_handlers [cur_block].AddFilter (code_len);
                }
 
                public virtual Label BeginExceptionBlock ()
                {
                        //System.Console.WriteLine ("Begin Block");
+                       if (open_blocks == null)
+                               open_blocks = new Stack (defaultExceptionStackSize);
                        
                        if (ex_handlers != null) {
                                cur_block = ex_handlers.Length;
@@ -344,14 +412,21 @@ namespace System.Reflection.Emit {
 
                public virtual void BeginFaultBlock()
                {
+                       if (open_blocks == null)
+                               open_blocks = new Stack (defaultExceptionStackSize);
+                       
                        if (open_blocks.Count <= 0)
                                throw new NotSupportedException ("Not in an exception block");
+                       InternalEndClause ();
                        //System.Console.WriteLine ("Begin fault Block");
-                       //throw new NotImplementedException ();
+                       ex_handlers [cur_block].AddFault (code_len);
                }
                
                public virtual void BeginFinallyBlock()
                {
+                       if (open_blocks == null)
+                               open_blocks = new Stack (defaultExceptionStackSize);
+                       
                        if (open_blocks.Count <= 0)
                                throw new NotSupportedException ("Not in an exception block");
                        InternalEndClause ();
@@ -360,17 +435,27 @@ 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)
                {
+                       return DeclareLocal (localType, false);
+               }
+
+
+#if NET_2_0
+               public
+#else
+               internal
+#endif
+               LocalBuilder DeclareLocal (Type localType, bool pinned)
+               {
+                       if (localType == null)
+                               throw new ArgumentNullException ("localType");
+
                        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);
@@ -412,6 +497,9 @@ namespace System.Reflection.Emit {
                        code [code_len++] = val;
                }
                
+#if NET_2_0
+               [ComVisible (true)]
+#endif
                public virtual void Emit (OpCode opcode, ConstructorInfo constructor)
                {
                        int token = token_gen.GetToken (constructor);
@@ -427,8 +515,6 @@ namespace System.Reflection.Emit {
                
                public virtual void Emit (OpCode opcode, double val)
                {
-                       Double.AssertEndianity (out val);
-
                        byte[] s = System.BitConverter.GetBytes (val);
                        make_room (10);
                        ll_emit (opcode);
@@ -497,7 +583,7 @@ namespace System.Reflection.Emit {
                        if (fixups == null)
                                fixups = new LabelFixup [defaultFixupSize]; 
                        else if (num_fixups >= fixups.Length) {
-                               LabelFixup[] newf = new LabelFixup [fixups.Length + 16];
+                               LabelFixup[] newf = new LabelFixup [fixups.Length * 2];
                                System.Array.Copy (fixups, newf, fixups.Length);
                                fixups = newf;
                        }
@@ -524,7 +610,7 @@ namespace System.Reflection.Emit {
                        if (fixups == null)
                                fixups = new LabelFixup [defaultFixupSize + count]; 
                        else if (num_fixups + count >= fixups.Length) {
-                               LabelFixup[] newf = new LabelFixup [fixups.Length + count + 16];
+                               LabelFixup[] newf = new LabelFixup [count + fixups.Length * 2];
                                System.Array.Copy (fixups, newf, fixups.Length);
                                fixups = newf;
                        }
@@ -619,8 +705,12 @@ namespace System.Reflection.Emit {
                        int token = token_gen.GetToken (method);
                        make_room (6);
                        ll_emit (opcode);
-                       if (method.DeclaringType.Module == module)
-                               add_token_fixup (method);
+                       Type declaringType = method.DeclaringType;
+                       // Might be a DynamicMethod with no declaring type
+                       if (declaringType != null) {
+                               if (declaringType.Module == module)
+                                       add_token_fixup (method);
+                       }
                        emit_int (token);
                        if (method.ReturnType != void_type)
                                cur_stack ++;
@@ -633,8 +723,12 @@ namespace System.Reflection.Emit {
                {
                        make_room (6);
                        ll_emit (opcode);
-                       if (method.DeclaringType.Module == module)
-                               add_token_fixup (method);
+                       // Might be a DynamicMethod with no declaring type
+                       Type declaringType = method.DeclaringType;
+                       if (declaringType != null) {
+                               if (declaringType.Module == module)
+                                       add_token_fixup (method);
+                       }
                        emit_int (token);
                        if (method.ReturnType != void_type)
                                cur_stack ++;
@@ -690,7 +784,7 @@ namespace System.Reflection.Emit {
                        emit_int (token_gen.GetToken (type));
                }
 
-               [MonoTODO ("Do something about varargs method")]
+               [MonoTODO ("vararg methods are not supported")]
                public void EmitCall (OpCode opcode, MethodInfo methodinfo, Type[] optionalParamTypes)
                {
                        if (methodinfo == null)
@@ -770,6 +864,9 @@ namespace System.Reflection.Emit {
 
                public virtual void EndExceptionBlock ()
                {
+                       if (open_blocks == null)
+                               open_blocks = new Stack (defaultExceptionStackSize);
+                       
                        if (open_blocks.Count <= 0)
                                throw new NotSupportedException ("Not in an exception block");
                        InternalEndClause ();
@@ -785,14 +882,7 @@ 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)
                {
@@ -808,10 +898,43 @@ namespace System.Reflection.Emit {
                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);
+                       if (currentSequence == null || currentSequence.Document != document) {
+                               if (sequencePointLists == null)
+                                       sequencePointLists = new ArrayList ();
+                               currentSequence = new SequencePointList (document);
+                               sequencePointLists.Add (currentSequence);
+                       }
+                       
+                       currentSequence.AddSequencePoint (code_len, startLine, startColumn, endLine, endColumn);
+               }
+               
+               internal void GenerateDebugInfo (ISymbolWriter symbolWriter)
+               {
+                       if (sequencePointLists != null) {
+                               SequencePointList first = (SequencePointList) sequencePointLists [0];
+                               SequencePointList last = (SequencePointList) sequencePointLists [sequencePointLists.Count - 1];
+                               symbolWriter.SetMethodSourceRange (first.Document, first.StartLine, first.StartColumn, last.Document, last.EndLine, last.EndColumn);
+                               
+                               foreach (SequencePointList list in sequencePointLists)
+                                       symbolWriter.DefineSequencePoints (list.Document, list.GetOffsets(), list.GetLines(), list.GetColumns(), list.GetEndLines(), list.GetEndColumns());
+                               
+                               if (locals != null) {
+                                       foreach (LocalBuilder local in locals) {
+                                               if (local.Name != null && local.Name.Length > 0) {
+                                                       SignatureHelper sighelper = SignatureHelper.GetLocalVarSigHelper (module);
+                                                       sighelper.AddArgument (local.LocalType);
+                                                       byte[] signature = sighelper.GetSignature ();
+                                                       symbolWriter.DefineLocalVariable (local.Name, FieldAttributes.Public, signature, SymAddressKind.ILOffset, local.position, 0, 0, local.StartOffset, local.EndOffset);
+                                               }
+                                       }
+                               }
+                               sequencePointLists = null;
+                       }
+               }
+               
+               internal bool HasDebugInfo
+               {
+                       get { return sequencePointLists != null; }
                }
 
                public virtual void ThrowException (Type exceptionType)
@@ -850,5 +973,119 @@ namespace System.Reflection.Emit {
                                }
                        }
                }
+
+               internal static int Mono_GetCurrentOffset (ILGenerator ig)
+               {
+                       return ig.code_len;
+               }
+
+               void _ILGenerator.GetIDsOfNames ([In] ref Guid riid, IntPtr rgszNames, uint cNames, uint lcid, IntPtr rgDispId)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               void _ILGenerator.GetTypeInfo (uint iTInfo, uint lcid, IntPtr ppTInfo)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               void _ILGenerator.GetTypeInfoCount (out uint pcTInfo)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               void _ILGenerator.Invoke (uint dispIdMember, [In] ref Guid riid, uint lcid, short wFlags, IntPtr pDispParams, IntPtr pVarResult, IntPtr pExcepInfo, IntPtr puArgErr)
+               {
+                       throw new NotImplementedException ();
+               }
+       }
+       
+       internal class SequencePointList
+       {
+               ISymbolDocumentWriter doc;
+               SequencePoint[] points;
+               int count;
+               const int arrayGrow = 10;
+               
+               public SequencePointList (ISymbolDocumentWriter doc)
+               {
+                       this.doc = doc;
+               }
+               
+               public ISymbolDocumentWriter Document {
+                       get { return doc; }
+               }
+               
+               public int[] GetOffsets()
+               {
+                       int[] data = new int [count];
+                       for (int n=0; n<count; n++) data [n] = points[n].Offset;
+                       return data; 
+               }
+               public int[] GetLines()
+               {
+                       int[] data = new int [count];
+                       for (int n=0; n<count; n++) data [n] = points[n].Line;
+                       return data; 
+               }
+               public int[] GetColumns()
+               {
+                       int[] data = new int [count];
+                       for (int n=0; n<count; n++) data [n] = points[n].Col;
+                       return data; 
+               }
+               public int[] GetEndLines()
+               {
+                       int[] data = new int [count];
+                       for (int n=0; n<count; n++) data [n] = points[n].EndLine;
+                       return data; 
+               }
+               public int[] GetEndColumns()
+               {
+                       int[] data = new int [count];
+                       for (int n=0; n<count; n++) data [n] = points[n].EndCol;
+                       return data; 
+               }
+               public int StartLine {
+                       get { return points[0].Line; }
+               }
+               public int EndLine {
+                       get { return points[count - 1].Line; }
+               }
+               public int StartColumn {
+                       get { return points[0].Col; }
+               }
+               public int EndColumn {
+                       get { return points[count - 1].Col; }
+               }
+               
+               public void AddSequencePoint (int offset, int line, int col, int endLine, int endCol)
+               {
+                       SequencePoint s = new SequencePoint ();
+                       s.Offset = offset;
+                       s.Line = line;
+                       s.Col = col;
+                       s.EndLine = endLine;
+                       s.EndCol = endCol;
+                       
+                       if (points == null) {
+                               points = new SequencePoint [arrayGrow];
+                       } else if (count >= points.Length) {
+                               SequencePoint[] temp = new SequencePoint [count + arrayGrow];
+                               Array.Copy (points, temp, points.Length);
+                               points = temp;
+                       }
+                       
+                       points [count] = s;
+                       count++;
+               }
+       }
+       
+       struct SequencePoint {
+               public int Offset;
+               public int Line;
+               public int Col;
+               public int EndLine;
+               public int EndCol;
        }
 }