//
// System.Diagnostics.StackFrame.cs
//
// Author:
// Alexander Klyubin (klyubin@aqris.com)
// Dietmar Maurer (dietmar@ximian.com)
//
// (C) 2001
//
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace System.Diagnostics {
///
/// Stack frame.
///
[Serializable]
public class StackFrame {
///
/// Constant returned when the native or IL offset is unknown.
///
public const int OFFSET_UNKNOWN = -1;
///
/// Offset from the start of the IL code for the method
/// being executed.
///
private int ilOffset = OFFSET_UNKNOWN;
///
/// Offset from the start of the native code for the method
/// being executed.
///
private int nativeOffset = OFFSET_UNKNOWN;
///
/// Method associated with this stack frame.
///
private MethodBase methodBase;
///
/// File name.
///
private string fileName;
///
/// Line number.
///
private int lineNumber;
///
/// Column number.
///
private int columnNumber;
#if TARGET_JVM
static bool get_frame_info (int skip, bool needFileInfo, out MethodBase method,
out int iloffset, out int native_offset,
out string file, out int line, out int column)
{
native_offset = 0;
line = 0;
column = 0;
file = "";
iloffset = 0;
method = null;
return false;
}
#else
[MethodImplAttribute(MethodImplOptions.InternalCall)]
extern static bool get_frame_info (int skip, bool needFileInfo, out MethodBase method,
out int iloffset, out int native_offset,
out string file, out int line, out int column);
#endif
///
/// Initializes a new StackFrame object corresponding to the
/// active stack frame.
///
public StackFrame()
{
get_frame_info (2, false, out methodBase, out ilOffset,
out nativeOffset, out fileName, out lineNumber,
out columnNumber);
}
///
/// Initializes a new StackFrame object corresponding to the
/// active stack frame.
///
///
/// TODO:
///
public StackFrame(bool needFileInfo) : this() {
get_frame_info (2, needFileInfo, out methodBase, out ilOffset,
out nativeOffset, out fileName, out lineNumber,
out columnNumber);
}
///
/// Initializes a new StackFrame object corresponding to the
/// active stack frame.
///
///
/// The number of frames up the stack to skip.
///
public StackFrame(int skipFrames) {
get_frame_info (skipFrames + 2, false, out methodBase, out ilOffset,
out nativeOffset, out fileName, out lineNumber,
out columnNumber);
}
///
/// Initializes a new StackFrame object corresponding to the
/// active stack frame.
///
///
/// The number of frames up the stack to skip.
///
///
/// TODO:
///
public StackFrame(int skipFrames, bool needFileInfo) {
get_frame_info (skipFrames + 2, needFileInfo, out methodBase, out ilOffset,
out nativeOffset, out fileName, out lineNumber,
out columnNumber);
}
///
/// Constructs a fake stack frame that just contains the
/// given file name and line number. Use this constructor
/// when you do not want to use the debugger's line mapping
/// logic.
///
///
/// The given file name.
///
///
/// The line number in the specified file.
///
// LAMESPEC: According to the MSDN docs, this creates a
// fake stack frame. But MS fills out the frame info as well
public StackFrame(string fileName, int lineNumber) {
get_frame_info (2, false, out methodBase, out ilOffset,
out nativeOffset, out fileName, out lineNumber,
out columnNumber);
this.fileName = fileName;
this.lineNumber = lineNumber;
this.columnNumber = 0;
}
///
/// Constructs a fake stack frame that just contains the
/// given file name and line number. Use this constructor
/// when you do not want to use the debugger's line mapping
/// logic.
///
///
/// The given file name.
///
///
/// The line number in the specified file.
///
///
/// The column number in the specified file.
///
// LAMESPEC: According to the MSDN docs, this creates a
// fake stack frame. But MS fills out the frame info as well
public StackFrame(string fileName,
int lineNumber,
int colNumber) {
get_frame_info (2, false, out methodBase, out ilOffset,
out nativeOffset, out fileName, out lineNumber,
out columnNumber);
this.fileName = fileName;
this.lineNumber = lineNumber;
this.columnNumber = colNumber;
}
///
/// Gets the line number in the file containing the code
/// being executed. This information is typically extracted
/// from the debugging symbols for the executable.
///
///
/// The file line number or zero if it cannot be determined.
///
public virtual int GetFileLineNumber()
{
return lineNumber;
}
///
/// Gets the column number in the file containing the code
/// being executed. This information is typically extracted
/// from the debugging symbols for the executable.
///
///
/// The file column number or zero if it cannot be determined.
///
public virtual int GetFileColumnNumber()
{
return columnNumber;
}
///
/// Gets the file name containing the code being executed.
/// This information is typically extracted from the
/// debugging symbols for the executable.
///
///
/// The file name or null if it cannot be determined.
///
public virtual string GetFileName()
{
return fileName;
}
///
/// Gets the offset from the start of the IL code for the
/// method being executed. This offset may be approximate
/// depending on whether the JIT compiler is generating
/// debugging code or not.
///
///
/// The offset from the start of the IL code for the method
/// being executed.
///
public virtual int GetILOffset()
{
return ilOffset;
}
///
/// Gets the method in which the frame is executing.
///
///
/// The method the frame is executing in.
///
public virtual MethodBase GetMethod()
{
return methodBase;
}
///
/// Gets the offset from the start of the native
/// (JIT-compiled) code for the method being executed.
///
///
/// The offset from the start of the native (JIT-compiled)
/// code or the method being executed.
///
public virtual int GetNativeOffset()
{
return nativeOffset;
}
///
/// Builds a readable representation of the stack frame.
///
///
/// A readable representation of the stack frame.
///
public override string ToString() {
string methodNameString =
(GetMethod() == null)
? ""
: GetMethod().Name;
string offsetString =
(GetILOffset() == OFFSET_UNKNOWN)
? ""
: "offset " + GetILOffset();
string fileNameString =
(GetFileName() == null)
? "" : GetFileName();
return methodNameString + " at " + offsetString
+ " in file:line:column " + fileNameString
+ ":" + GetFileLineNumber()
+ ":" + GetFileColumnNumber();
}
public override bool Equals(Object obj) {
if ((obj == null) || (!(obj is StackFrame))) {
return false;
}
StackFrame rhs = (StackFrame) obj;
if (!ObjectsEqual(GetMethod(), rhs.GetMethod())) {
return false;
}
if (!ObjectsEqual(GetFileName(), rhs.GetFileName())) {
return false;
}
if (GetFileLineNumber() != rhs.GetFileLineNumber()) {
return false;
}
if (GetFileColumnNumber() != rhs.GetFileColumnNumber()) {
return false;
}
if (GetILOffset() != rhs.GetILOffset()) {
return false;
}
if (GetNativeOffset() != rhs.GetNativeOffset()) {
return false;
}
return true;
}
public override int GetHashCode() {
return GetFileLineNumber();
}
///
/// Checks whether two objects are equal.
/// The objects are assumed equal if and only if either
/// both of the references are null
or they
/// equal via Equals
method.
///
///
/// First object.
///
///
/// Second object.
///
///
/// true
if the two objects are equal,
/// false otherwise.
///
private static bool ObjectsEqual(Object obj1, Object obj2) {
if (obj1 == null) {
return (obj2 == null);
} else {
return obj1.Equals(obj2);
}
}
}
}