2007-11-30 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mcs / class / corlib / System.Diagnostics / StackTrace.cs
index 88dfe2e28bdd99cdc608fed7c9ea3c54c8d453c1..404056c2c4e3bb4ef699abd03be3e76e5760b2f3 100644 (file)
@@ -6,10 +6,7 @@
 //      Dietmar Maurer (dietmar@ximian.com)
 //
 // (C) 2001
-//
-
-//
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004-2005 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
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System;
+using System.Collections;
+using System.Globalization;
 using System.Reflection;
-using System.Threading;
 using System.Runtime.CompilerServices;
-using System.Collections;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Security.Permissions;
+using System.Text;
+using System.Threading;
 
 namespace System.Diagnostics {
-        /// <summary>
-        ///   Stack trace.
-        ///   TODO: more information.
-        /// </summary>
-        [Serializable]
-       [MonoTODO ("Fix serialization compatibility with MS.NET")]
+
+       [Serializable]
+#if NET_2_0
+       [ComVisible (true)]
+#endif
+       [MonoTODO ("Serialized objects are not compatible with .NET")]
        public class StackTrace {
-                /// <value>
-                ///   Uses a constant to define the number of methods that are
-                ///   to be omitted from the stack trace.
-                /// </value>
-                public const int METHODS_TO_SKIP = 0;
-                
-                /// <value>
-                ///   Frames. First frame is the last stack frame pushed.
-                /// </value>
-                private StackFrame[] frames;
 
-                /// <summary>
-                ///   Initializes a new instance of the StackTrace class.
-                /// </summary>
-               [MonoTODO]
-                public StackTrace() {
+               public const int METHODS_TO_SKIP = 0;
+
+               private StackFrame[] frames;
+               private bool debug_info;
+
+               public StackTrace ()
+               {
                        init_frames (METHODS_TO_SKIP, false);
                }
-                
-                /// <summary>
-                ///   Initializes a new instance of the StackTrace class.
-                /// </summary>
-                /// <param name="needFileInfo">
-                ///   TODO:
-                /// </param>
-                public StackTrace(bool needFileInfo) {
+
+               public StackTrace (bool needFileInfo)
+               {
                        init_frames (METHODS_TO_SKIP, needFileInfo);
                }
 
-                /// <summary>
-                ///   Initializes a new instance of the StackTrace class
-                ///   from the current location, in a caller's frame.
-                /// </summary>
-                /// <param name="skipFrames">
-                ///   The number of frames up the stack to start the trace
-                ///   from.
-                /// </param>
-                public StackTrace(int skipFrames) {
+               public StackTrace (int skipFrames)
+               {
                        init_frames (skipFrames, false);
                }
 
-               /// <summary>
-                ///   Initializes a new instance of the StackTrace class
-                ///   from the current location, in a caller's frame.
-                /// </summary>
-                /// <param name="skipFrames">
-                ///   The number of frames up the stack to start the trace
-                ///   from.
-                /// </param>
-                /// <param name="needFileInfo">
-                ///   TODO:
-                /// </param>
-                public StackTrace(int skipFrames, bool needFileInfo) {
+               public StackTrace (int skipFrames, bool needFileInfo)
+               {
                        init_frames (skipFrames, needFileInfo);
                }
 
                void init_frames (int skipFrames, bool needFileInfo)
                {
+                       if (skipFrames < 0)
+                               throw new ArgumentOutOfRangeException ("< 0", "skipFrames");
+
                        StackFrame sf;
                        ArrayList al = new ArrayList ();
 
@@ -115,197 +89,163 @@ namespace System.Diagnostics {
                                skipFrames++;
                        };
 
-                        frames = (StackFrame [])al.ToArray (typeof (StackFrame));      
+                       debug_info = needFileInfo;
+                       frames = (StackFrame [])al.ToArray (typeof (StackFrame));       
                }
                
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                extern static StackFrame [] get_trace (Exception e, int skipFrames, bool needFileInfo);
 
-                /// <summary>
-                ///   Initializes a new instance of the StackTrace class.
-                /// </summary>
-                /// <param name="e">
-                ///   TODO:
-                /// </param>
-                public StackTrace(Exception e) {
-                        frames = get_trace (e, METHODS_TO_SKIP, false);
-                }
-                                
-                /// <summary>
-                ///   Initializes a new instance of the StackTrace class,
-                ///   using the provided exception object. The resulting stack
-                ///   trace describes the stack at the time of the exception.
-                /// </summary>
-                /// <param name="e">
-                ///   TODO:
-                /// </param>
-                /// <param name="needFileInfo">
-                ///   TODO:
-                /// </param>
-                public StackTrace(Exception e, bool needFileInfo) {
-                        frames = get_trace (e, METHODS_TO_SKIP, needFileInfo);
+               public StackTrace (Exception e)
+                       : this (e, METHODS_TO_SKIP, false)
+               {
                }
-                
-                /// <summary>
-                ///   Initializes a new instance of the StackTrace class,
-                ///   using the provided exception object. The resulting stack
-                ///   trace describes the stack at the time of the exception.
-                /// </summary>
-                /// <param name="e">
-                ///   Exception.
-                /// </param>
-                /// <param name="skipFrames">
-                ///   The number of frames up the stack to start the trace
-                ///   from.
-                /// </param>
-                public StackTrace(Exception e, int skipFrames) {
-                        frames = get_trace (e, skipFrames, false);
-                }
-                
-                /// <summary>
-                ///   Initializes a new instance of the StackTrace class,
-                ///   using the provided exception object. The resulting stack
-                ///   trace describes the stack at the time of the exception.
-                /// </summary>
-                /// <param name="e">
-                ///   Exception.
-                /// </param>
-                /// <param name="skipFrames">
-                ///   The number of frames up the stack to start the trace
-                ///   from.
-                /// </param>
-                /// <param name="needFileInfo">
-                ///   TODO:
-                /// </param>
-                public StackTrace(Exception e, int skipFrames, bool needFileInfo) {
-                        frames = get_trace (e, skipFrames, needFileInfo);
+
+               public StackTrace (Exception e, bool needFileInfo)
+                       : this (e, METHODS_TO_SKIP, needFileInfo)
+               {
                }
-                              
-                /// <summary>
-                ///   Initializes a new instance of the StackTrace class
-                ///   containing a single frame.
-                /// </summary>
-                /// <param name="frame">
-                ///   The frame that the StackTrace object should contain.
-                /// </param>
-                public StackTrace(StackFrame frame) {
-                        this.frames = new StackFrame[1];
-                        this.frames[0] = frame;
-                }
-                               
-                /// <summary>
-                ///   Initializes a new instance of the StackTrace class.
-                /// </summary>
-                /// <param name="targetThread">
-                ///   TODO:
-                /// </param>
-                /// <param name="needFileInfo">
-                ///   TODO:
-                /// </param>
-               [MonoTODO]
-                public StackTrace(Thread targetThread, bool needFileInfo) {
-                        throw new NotImplementedException();
-                }
-               
-                /// <summary>
-                ///   Holds the number of frames in the stack trace.
-                /// </summary>
-                public virtual int FrameCount {
-                        get {
-                                return (frames == null) ? 0 : frames.Length;
-                        }
-                }             
-                              
-                /// <summary>
-                ///   Gets the specified stack frame.
-                /// </summary>
-                /// <param name="index">
-                ///   The index of the stack frame requested.
-                /// </param>
-                /// <returns>
-                ///   The specified stack frame. Returns <code>null</code> if
-                ///   frame with specified index does not exist in this stack
-                ///   trace.
-                /// </returns>
-                /// <remarks>
-                ///   Stack frames are numbered starting at zero, which is the
-                ///   last stack frame pushed.
-                /// </remarks>
-                public virtual StackFrame GetFrame(int index) {
-                        if ((index < 0) || (index >= FrameCount)) {
-                                return null;
-                        }
-                        
-                        return frames[index];
-                }              
-                
-                /// <summary>
-                ///   Builds a readable representation of the stack trace.
-                /// </summary>
-                /// <returns>
-                ///   A readable representation of the stack trace.
-                /// </returns>
-                public override string ToString() {
-                        string result = "";
-                        for (int i = 0; i < FrameCount; i++) {
-                                StackFrame frame = GetFrame(i);
-                                result += "\n\tat " + FrameToString(frame);
-                        }
-                        
-                        return result;
-                }
 
-               //
-               // These are not on the Framework
-               //
-#if false
-               
-                public override bool Equals(Object obj) {
-                        if ((obj == null) || (!(obj is StackTrace))) {
-                                return false;
-                        }
-                        
-                        StackTrace rhs = (StackTrace) obj;
-                        
-                        if (FrameCount != rhs.FrameCount) {
-                                return false;
-                        }
-                        
-                        for (int i = 0; i < FrameCount; i++) {
-                                if (!GetFrame(i).Equals(rhs.GetFrame(i))) {
-                                        return false;
-                                }
-                        }
-                        
-                        return true;
-                }
-                
-                public override int GetHashCode() {
-                        return FrameCount;
-                }
+               public StackTrace (Exception e, int skipFrames)
+                       : this (e, skipFrames, false)
+               {
+               }
+
+               public StackTrace (Exception e, int skipFrames, bool needFileInfo)
+                       : this (e, skipFrames, needFileInfo, false)
+               {
+               }
+
+               internal StackTrace (Exception e, int skipFrames, bool needFileInfo, bool returnNativeFrames)
+               {
+                       if (e == null)
+                               throw new ArgumentNullException ("e");
+                       if (skipFrames < 0)
+                               throw new ArgumentOutOfRangeException ("< 0", "skipFrames");
+
+                       frames = get_trace (e, skipFrames, needFileInfo);
+
+                       if (!returnNativeFrames) {
+                               bool resize = false;
+                               for (int i = 0; i < frames.Length; ++i)
+                                       if (frames [i].GetMethod () == null)
+                                               resize = true;
+
+                               if (resize) {
+                                       ArrayList al = new ArrayList ();
+
+                                       for (int i = 0; i < frames.Length; ++i)
+                                               if (frames [i].GetMethod () != null)
+                                                       al.Add (frames [i]);
+
+                                       frames = (StackFrame [])al.ToArray (typeof (StackFrame));
+                               }
+                       }
+               }
+
+#if ONLY_1_1
+               [ReflectionPermission (SecurityAction.Demand, TypeInformation = true)]
 #endif
-                
-                /// <summary>
-                ///   Converts single stack frame to string to be used in
-                ///   ToString method.
-                /// </summary>
-                /// <param name="frame">
-                ///   Frame to convert.
-                /// </param>
-                /// <returns>
-                ///   A readable representation of stack frame for using
-                ///   ToString.
-                /// </returns>
-                private static String FrameToString(StackFrame frame) {
-                       MethodBase method = frame.GetMethod();
-                        if (method != null) {
-                                // Method information available
-                                return  method.DeclaringType.FullName
-                                        + "." + method.Name + "()";
-                        } else {
-                                // Method information not available
-                                return "<unknown method>";
-                        }
-                }
-        }
+               public StackTrace (StackFrame frame)
+               {
+                       this.frames = new StackFrame [1];
+                       this.frames [0] = frame;
+               }
+
+#if ONLY_1_1
+               [ReflectionPermission (SecurityAction.Demand, TypeInformation = true)]
+#endif
+               [MonoTODO ("Not possible to create StackTraces from other threads")]
+               public StackTrace (Thread targetThread, bool needFileInfo)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               public virtual int FrameCount {
+                       get {
+                               return (frames == null) ? 0 : frames.Length;
+                       }
+               }
+
+               public virtual StackFrame GetFrame (int index)
+               {
+                       if ((index < 0) || (index >= FrameCount)) {
+                               return null;
+                       }
+
+                       return frames [index];
+               }
+
+#if NET_2_0
+               [ComVisibleAttribute (false)]
+               public virtual
+#else
+               // used for CAS implementation (before Fx 2.0)
+               internal
+#endif
+               StackFrame[] GetFrames ()
+               {
+                       return frames;
+               }
+
+               public override string ToString ()
+               {
+                       string newline = String.Format ("{0}   {1} ", Environment.NewLine, Locale.GetText ("at"));
+                       string unknown = Locale.GetText ("<unknown method>");
+                       string debuginfo = Locale.GetText (" in {0}:line {1}");
+                       StringBuilder sb = new StringBuilder ();
+                       for (int i = 0; i < FrameCount; i++) {
+                               StackFrame frame = GetFrame (i);
+                               if (i > 0)
+                                       sb.Append (newline);
+                               else
+                                       sb.AppendFormat ("   {0} ", Locale.GetText ("at"));
+                               MethodBase method = frame.GetMethod ();
+                               if (method != null) {
+                                       // Method information available
+                                       sb.AppendFormat ("{0}.{1}", method.DeclaringType.FullName, method.Name);
+                                       /* Append parameter information */
+                                       sb.Append ("(");
+                                       ParameterInfo[] p = method.GetParameters ();
+                                       for (int j = 0; j < p.Length; ++j) {
+                                               if (j > 0)
+                                                       sb.Append (", ");
+                                               Type pt = p[j].ParameterType;
+                                               bool byref = pt.IsByRef;
+                                               if (byref)
+                                                       pt = pt.GetElementType ();
+                                               if (pt.IsClass && pt.Namespace != String.Empty) {
+                                                       sb.Append (pt.Namespace);
+                                                       sb.Append (".");
+                                               }
+                                               sb.Append (pt.Name);
+                                               if (byref)
+                                                       sb.Append (" ByRef");
+                                               sb.AppendFormat (" {0}", p [j].Name);
+                                       }
+                                       sb.Append (")");
+                               }
+                               else {
+                                       // Method information not available
+                                       sb.Append (unknown);
+                               }
+
+                               if (debug_info) {
+                                       // we were asked for debugging informations
+                                       try {
+                                               // but that doesn't mean we have the debug information available
+                                               string fname = frame.GetFileName ();
+                                               if ((fname != null) && (fname.Length > 0))
+                                                       sb.AppendFormat (debuginfo, fname, frame.GetFileLineNumber ());
+                                       }
+                                       catch (SecurityException) {
+                                               // don't leak information (about the filename) if the security 
+                                               // manager doesn't allow it (but don't loop on this exception)
+                                               debug_info = false;
+                                       }
+                               }
+                       }
+                       return sb.ToString ();
+               }
+       }
 }