using System;
using System.Text;
using System.Threading;
using Mono.Debugging.Backend;
namespace Mono.Debugging.Client
{
[Serializable]
public class StackFrame
{
long address;
string addressSpace;
SourceLocation location;
IBacktrace sourceBacktrace;
string language;
int index;
bool isExternalCode;
bool isDebuggerHidden;
bool hasDebugInfo;
string fullModuleName;
string fullTypeName;
[NonSerialized]
DebuggerSession session;
public StackFrame (long address, string addressSpace, SourceLocation location, string language, bool isExternalCode, bool hasDebugInfo, bool isDebuggerHidden, string fullModuleName, string fullTypeName)
{
this.address = address;
this.addressSpace = addressSpace;
this.location = location;
this.language = language;
this.isExternalCode = isExternalCode;
this.isDebuggerHidden = isDebuggerHidden;
this.hasDebugInfo = hasDebugInfo;
this.fullModuleName = fullModuleName;
this.fullTypeName = fullTypeName;
}
public StackFrame (long address, string addressSpace, SourceLocation location, string language, bool isExternalCode, bool hasDebugInfo, string fullModuleName, string fullTypeName)
: this (address, addressSpace, location, language, isExternalCode, hasDebugInfo, false, fullModuleName, fullTypeName)
{
}
[Obsolete]
public StackFrame (long address, string addressSpace, SourceLocation location, string language)
: this (address, addressSpace, location, language, string.IsNullOrEmpty (location.FileName), true, "", "")
{
}
[Obsolete]
public StackFrame (long address, string addressSpace, string module, string method, string filename, int line, string language)
: this (address, addressSpace, new SourceLocation (method, filename, line), language)
{
}
public StackFrame (long address, SourceLocation location, string language, bool isExternalCode, bool hasDebugInfo)
: this (address, "", location, language, string.IsNullOrEmpty (location.FileName) || isExternalCode, hasDebugInfo, "", "")
{
}
public StackFrame (long address, SourceLocation location, string language)
: this (address, "", location, language, string.IsNullOrEmpty (location.FileName), true, "", "")
{
}
internal void Attach (DebuggerSession debugSession)
{
session = debugSession;
}
public DebuggerSession DebuggerSession {
get { return session; }
}
public SourceLocation SourceLocation {
get { return location; }
}
public long Address {
get { return address; }
}
public string AddressSpace {
get { return addressSpace; }
}
internal IBacktrace SourceBacktrace {
get { return sourceBacktrace; }
set { sourceBacktrace = value; }
}
public int Index {
get { return index; }
internal set { index = value; }
}
public string Language {
get { return language; }
}
public bool IsExternalCode {
get { return isExternalCode; }
}
public bool IsDebuggerHidden {
get { return isDebuggerHidden; }
}
public bool HasDebugInfo {
get { return hasDebugInfo; }
}
public string FullModuleName {
get { return fullModuleName; }
}
public string FullTypeName {
get { return fullTypeName; }
}
///
/// Gets the full name of the stackframe. Which respects Session.EvaluationOptions.StackFrameFormat
///
public virtual string FullStackframeText {
get {
var methodNameBuilder = new StringBuilder (this.SourceLocation.MethodName);
var options = session.EvaluationOptions.Clone ();
if (options.StackFrameFormat.ParameterValues) {
options.AllowMethodEvaluation = true;
options.AllowToStringCalls = true;
options.AllowTargetInvoke = true;
} else {
options.AllowMethodEvaluation = false;
options.AllowToStringCalls = false;
options.AllowTargetInvoke = false;
}
var args = this.GetParameters (options);
//MethodName starting with "["... it's something like [ExternalCode]
if (!this.SourceLocation.MethodName.StartsWith ("[", StringComparison.Ordinal)) {
if (options.StackFrameFormat.Module && !string.IsNullOrEmpty (this.FullModuleName)) {
methodNameBuilder.Insert (0, System.IO.Path.GetFileName (this.FullModuleName) + "!");
}
if (options.StackFrameFormat.ParameterTypes || options.StackFrameFormat.ParameterNames || options.StackFrameFormat.ParameterValues) {
methodNameBuilder.Append ("(");
for (int n = 0; n < args.Length; n++) {
if (args [n].IsEvaluating) {
var manualReset = new ManualResetEvent (false);
EventHandler updated = (s, e) => {
manualReset.Set ();
};
args [n].ValueChanged += updated;
if (args [n].IsEvaluating)
if (!manualReset.WaitOne (2000))
session.OnDebuggerOutput (true, "Timeout evaluating parameters for StackFrame.");
args [n].ValueChanged -= updated;
}
if (n > 0)
methodNameBuilder.Append (", ");
if (options.StackFrameFormat.ParameterTypes) {
methodNameBuilder.Append (args [n].TypeName);
if (options.StackFrameFormat.ParameterNames)
methodNameBuilder.Append (" ");
}
if (options.StackFrameFormat.ParameterNames)
methodNameBuilder.Append (args [n].Name);
if (options.StackFrameFormat.ParameterValues) {
if (options.StackFrameFormat.ParameterTypes || options.StackFrameFormat.ParameterNames)
methodNameBuilder.Append (" = ");
var val = args [n].Value ?? "";
methodNameBuilder.Append (val.Replace ("\r\n", " ").Replace ("\n", " "));
}
}
methodNameBuilder.Append (")");
}
}
return methodNameBuilder.ToString ();
}
}
public ObjectValue[] GetLocalVariables ()
{
return GetLocalVariables (session.EvaluationOptions);
}
public ObjectValue[] GetLocalVariables (EvaluationOptions options)
{
if (!hasDebugInfo) {
DebuggerLoggingService.LogMessage ("Cannot get local variables: no debugging symbols for frame: {0}", this);
return new ObjectValue [0];
}
var values = sourceBacktrace.GetLocalVariables (index, options);
ObjectValue.ConnectCallbacks (this, values);
return values;
}
public ObjectValue[] GetParameters ()
{
return GetParameters (session.EvaluationOptions);
}
public ObjectValue[] GetParameters (EvaluationOptions options)
{
if (!hasDebugInfo) {
DebuggerLoggingService.LogMessage ("Cannot get parameters: no debugging symbols for frame: {0}", this);
return new ObjectValue [0];
}
var values = sourceBacktrace.GetParameters (index, options);
ObjectValue.ConnectCallbacks (this, values);
return values;
}
public ObjectValue[] GetAllLocals ()
{
if (!hasDebugInfo) {
DebuggerLoggingService.LogMessage ("Cannot get local variables: no debugging symbols for frame: {0}", this);
return new ObjectValue [0];
}
var evaluator = session.FindExpressionEvaluator (this);
return evaluator != null ? evaluator.GetLocals (this) : GetAllLocals (session.EvaluationOptions);
}
public ObjectValue[] GetAllLocals (EvaluationOptions options)
{
if (!hasDebugInfo) {
DebuggerLoggingService.LogMessage ("Cannot get local variables: no debugging symbols for frame: {0}", this);
return new ObjectValue [0];
}
var values = sourceBacktrace.GetAllLocals (index, options);
ObjectValue.ConnectCallbacks (this, values);
return values;
}
public ObjectValue GetThisReference ()
{
return GetThisReference (session.EvaluationOptions);
}
public ObjectValue GetThisReference (EvaluationOptions options)
{
if (!hasDebugInfo) {
DebuggerLoggingService.LogMessage ("Cannot get `this' reference: no debugging symbols for frame: {0}", this);
return null;
}
var value = sourceBacktrace.GetThisReference (index, options);
if (value != null)
ObjectValue.ConnectCallbacks (this, value);
return value;
}
public ExceptionInfo GetException ()
{
return GetException (session.EvaluationOptions);
}
public ExceptionInfo GetException (EvaluationOptions options)
{
var value = sourceBacktrace.GetException (index, options);
if (value != null)
value.ConnectCallback (this);
return value;
}
public string ResolveExpression (string exp)
{
return session.ResolveExpression (exp, location);
}
public ObjectValue[] GetExpressionValues (string[] expressions, bool evaluateMethods)
{
var options = session.EvaluationOptions.Clone ();
options.AllowMethodEvaluation = evaluateMethods;
return GetExpressionValues (expressions, options);
}
public ObjectValue[] GetExpressionValues (string[] expressions, EvaluationOptions options)
{
if (!hasDebugInfo) {
DebuggerLoggingService.LogMessage ("Cannot get expression values: no debugging symbols for frame: {0}", this);
var vals = new ObjectValue [expressions.Length];
for (int n = 0; n < expressions.Length; n++)
vals[n] = ObjectValue.CreateUnknown (expressions[n]);
return vals;
}
if (options.UseExternalTypeResolver) {
var resolved = new string [expressions.Length];
for (int n = 0; n < expressions.Length; n++)
resolved[n] = ResolveExpression (expressions[n]);
expressions = resolved;
}
var values = sourceBacktrace.GetExpressionValues (index, expressions, options);
ObjectValue.ConnectCallbacks (this, values);
return values;
}
public ObjectValue GetExpressionValue (string expression, bool evaluateMethods)
{
var options = session.EvaluationOptions.Clone ();
options.AllowMethodEvaluation = evaluateMethods;
return GetExpressionValue (expression, options);
}
public ObjectValue GetExpressionValue (string expression, EvaluationOptions options)
{
if (!hasDebugInfo) {
DebuggerLoggingService.LogMessage ("Cannot get expression value: no debugging symbols for frame: {0}", this);
return ObjectValue.CreateUnknown (expression);
}
if (options.UseExternalTypeResolver)
expression = ResolveExpression (expression);
var values = sourceBacktrace.GetExpressionValues (index, new [] { expression }, options);
ObjectValue.ConnectCallbacks (this, values);
return values [0];
}
///
/// Returns True if the expression is valid and can be evaluated for this frame.
///
public bool ValidateExpression (string expression)
{
return ValidateExpression (expression, session.EvaluationOptions);
}
///
/// Returns True if the expression is valid and can be evaluated for this frame.
///
public ValidationResult ValidateExpression (string expression, EvaluationOptions options)
{
if (options.UseExternalTypeResolver)
expression = ResolveExpression (expression);
return sourceBacktrace.ValidateExpression (index, expression, options);
}
public CompletionData GetExpressionCompletionData (string exp)
{
return hasDebugInfo ? sourceBacktrace.GetExpressionCompletionData (index, exp) : null;
}
// Returns disassembled code for this stack frame.
// firstLine is the relative code line. It can be negative.
public AssemblyLine[] Disassemble (int firstLine, int count)
{
return sourceBacktrace.Disassemble (index, firstLine, count);
}
public override string ToString()
{
string loc;
if (location.Line != -1 && !string.IsNullOrEmpty (location.FileName)) {
loc = " at " + location.FileName + ":" + location.Line;
if (location.Column != 0)
loc += "," + location.Column;
} else if (!string.IsNullOrEmpty (location.FileName)) {
loc = " at " + location.FileName;
} else {
loc = string.Empty;
}
return string.Format ("0x{0:X} in {1}{2}", address, location.MethodName, loc);
}
public void UpdateSourceFile (string newFilePath)
{
location = new SourceLocation (location.MethodName, newFilePath, location.Line, location.Column, location.EndLine, location.EndColumn, location.FileHash, location.SourceLink);
}
}
[Serializable]
public struct ValidationResult
{
readonly string message;
readonly bool isValid;
public ValidationResult (bool isValid, string message)
{
this.isValid = isValid;
this.message = message;
}
public bool IsValid { get { return isValid; } }
public string Message { get { return message; } }
public static implicit operator bool (ValidationResult result)
{
return result.isValid;
}
}
}