Browse Source
git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5115 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61shortcuts
12 changed files with 910 additions and 974 deletions
@ -1,349 +0,0 @@ |
|||||||
// <file>
|
|
||||||
// <copyright see="prj:///doc/copyright.txt"/>
|
|
||||||
// <license see="prj:///doc/license.txt"/>
|
|
||||||
// <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
|
|
||||||
// <version>$Revision$</version>
|
|
||||||
// </file>
|
|
||||||
|
|
||||||
using ICSharpCode.NRefactory.Ast; |
|
||||||
using System; |
|
||||||
using System.Collections.Generic; |
|
||||||
|
|
||||||
namespace Debugger |
|
||||||
{ |
|
||||||
internal enum DebuggeeStateAction { Keep, Clear } |
|
||||||
|
|
||||||
public partial class Process |
|
||||||
{ |
|
||||||
internal bool TerminateCommandIssued = false; |
|
||||||
internal Queue<Breakpoint> BreakpointHitEventQueue = new Queue<Breakpoint>(); |
|
||||||
internal Dictionary<INode, Value> CachedExpressions = new Dictionary<INode, Value>(); |
|
||||||
|
|
||||||
#region Events
|
|
||||||
|
|
||||||
public event EventHandler<ProcessEventArgs> Paused; |
|
||||||
public event EventHandler<ProcessEventArgs> Resumed; |
|
||||||
|
|
||||||
// HACK: public
|
|
||||||
public virtual void OnPaused() |
|
||||||
{ |
|
||||||
AssertPaused(); |
|
||||||
// No real purpose - just additional check
|
|
||||||
if (callbackInterface.IsInCallback) throw new DebuggerException("Can not raise event within callback."); |
|
||||||
TraceMessage ("Debugger event: OnPaused()"); |
|
||||||
if (Paused != null) { |
|
||||||
foreach(Delegate d in Paused.GetInvocationList()) { |
|
||||||
if (IsRunning) { |
|
||||||
TraceMessage ("Skipping OnPaused delegate because process has resumed"); |
|
||||||
break; |
|
||||||
} |
|
||||||
if (this.TerminateCommandIssued || this.HasExited) { |
|
||||||
TraceMessage ("Skipping OnPaused delegate because process has exited"); |
|
||||||
break; |
|
||||||
} |
|
||||||
d.DynamicInvoke(this, new ProcessEventArgs(this)); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
protected virtual void OnResumed() |
|
||||||
{ |
|
||||||
AssertRunning(); |
|
||||||
// No real purpose - just additional check
|
|
||||||
if (callbackInterface.IsInCallback) throw new DebuggerException("Can not raise event within callback."); |
|
||||||
TraceMessage ("Debugger event: OnResumed()"); |
|
||||||
if (Resumed != null) { |
|
||||||
Resumed(this, new ProcessEventArgs(this)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region PauseSession & DebugeeState
|
|
||||||
|
|
||||||
PauseSession pauseSession; |
|
||||||
DebuggeeState debuggeeState; |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indentification of the current debugger session. This value changes whenever debugger is continued
|
|
||||||
/// </summary>
|
|
||||||
public PauseSession PauseSession { |
|
||||||
get { |
|
||||||
return pauseSession; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indentification of the state of the debugee. This value changes whenever the state of the debugee significatntly changes
|
|
||||||
/// </summary>
|
|
||||||
public DebuggeeState DebuggeeState { |
|
||||||
get { |
|
||||||
return debuggeeState; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary> Puts the process into a paused state </summary>
|
|
||||||
internal void NotifyPaused(PausedReason pauseReason) |
|
||||||
{ |
|
||||||
AssertRunning(); |
|
||||||
pauseSession = new PauseSession(this, pauseReason); |
|
||||||
if (debuggeeState == null) { |
|
||||||
debuggeeState = new DebuggeeState(this); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary> Puts the process into a resumed state </summary>
|
|
||||||
internal void NotifyResumed(DebuggeeStateAction action) |
|
||||||
{ |
|
||||||
AssertPaused(); |
|
||||||
pauseSession = null; |
|
||||||
if (action == DebuggeeStateAction.Clear) { |
|
||||||
if (debuggeeState == null) throw new DebuggerException("Debugee state already cleared"); |
|
||||||
debuggeeState = null; |
|
||||||
this.CachedExpressions.Clear(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary> Sets up the eviroment and raises user events </summary>
|
|
||||||
internal void RaisePausedEvents() |
|
||||||
{ |
|
||||||
DisableAllSteppers(); |
|
||||||
CheckSelectedStackFrames(); |
|
||||||
SelectMostRecentStackFrameWithLoadedSymbols(); |
|
||||||
|
|
||||||
if (this.PauseSession.PausedReason == PausedReason.Exception) { |
|
||||||
ExceptionEventArgs args = new ExceptionEventArgs(this, this.SelectedThread.CurrentException, this.SelectedThread.CurrentExceptionType, this.SelectedThread.CurrentExceptionIsUnhandled); |
|
||||||
OnExceptionThrown(args); |
|
||||||
// The event could have resumed or killed the process
|
|
||||||
if (this.IsRunning || this.TerminateCommandIssued || this.HasExited) return; |
|
||||||
} |
|
||||||
|
|
||||||
while(BreakpointHitEventQueue.Count > 0) { |
|
||||||
Breakpoint breakpoint = BreakpointHitEventQueue.Dequeue(); |
|
||||||
breakpoint.NotifyHit(); |
|
||||||
// The event could have resumed or killed the process
|
|
||||||
if (this.IsRunning || this.TerminateCommandIssued || this.HasExited) return; |
|
||||||
} |
|
||||||
|
|
||||||
OnPaused(); |
|
||||||
// The event could have resumed the process
|
|
||||||
if (this.IsRunning || this.TerminateCommandIssued || this.HasExited) return; |
|
||||||
} |
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Exceptions
|
|
||||||
|
|
||||||
bool pauseOnHandledException = false; |
|
||||||
|
|
||||||
public event EventHandler<ExceptionEventArgs> ExceptionThrown; |
|
||||||
|
|
||||||
public bool PauseOnHandledException { |
|
||||||
get { |
|
||||||
return pauseOnHandledException; |
|
||||||
} |
|
||||||
set { |
|
||||||
pauseOnHandledException = value; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
protected internal virtual void OnExceptionThrown(ExceptionEventArgs e) |
|
||||||
{ |
|
||||||
TraceMessage ("Debugger event: OnExceptionThrown()"); |
|
||||||
if (ExceptionThrown != null) { |
|
||||||
ExceptionThrown(this, e); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
internal void AssertPaused() |
|
||||||
{ |
|
||||||
if (IsRunning) { |
|
||||||
throw new DebuggerException("Process is not paused."); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
internal void AssertRunning() |
|
||||||
{ |
|
||||||
if (IsPaused) { |
|
||||||
throw new DebuggerException("Process is not running."); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public bool IsRunning { |
|
||||||
get { |
|
||||||
return pauseSession == null; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public bool IsPaused { |
|
||||||
get { |
|
||||||
return !IsRunning; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void Break() |
|
||||||
{ |
|
||||||
AssertRunning(); |
|
||||||
|
|
||||||
corProcess.Stop(uint.MaxValue); // Infinite; ignored anyway
|
|
||||||
|
|
||||||
NotifyPaused(PausedReason.ForcedBreak); |
|
||||||
RaisePausedEvents(); |
|
||||||
} |
|
||||||
|
|
||||||
public void Detach() |
|
||||||
{ |
|
||||||
if (IsRunning) { |
|
||||||
corProcess.Stop(uint.MaxValue); |
|
||||||
NotifyPaused(PausedReason.ForcedBreak); |
|
||||||
} |
|
||||||
corProcess.Detach(); |
|
||||||
NotifyHasExited(); |
|
||||||
} |
|
||||||
|
|
||||||
#region Convenience methods
|
|
||||||
|
|
||||||
public void Continue() |
|
||||||
{ |
|
||||||
AsyncContinue(); |
|
||||||
WaitForPause(); |
|
||||||
} |
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public void AsyncContinue() |
|
||||||
{ |
|
||||||
AsyncContinue(DebuggeeStateAction.Clear); |
|
||||||
} |
|
||||||
|
|
||||||
internal void AsyncContinue(DebuggeeStateAction action) |
|
||||||
{ |
|
||||||
AssertPaused(); |
|
||||||
|
|
||||||
NotifyResumed(action); |
|
||||||
corProcess.Continue(0); |
|
||||||
if (this.Options.Verbose) { |
|
||||||
this.TraceMessage("Continue"); |
|
||||||
} |
|
||||||
|
|
||||||
if (action == DebuggeeStateAction.Clear) { |
|
||||||
OnResumed(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary> Terminates the execution of the process </summary>
|
|
||||||
public void Terminate() |
|
||||||
{ |
|
||||||
AsyncTerminate(); |
|
||||||
// Wait until ExitProcess callback is received
|
|
||||||
WaitForExit(); |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary> Terminates the execution of the process </summary>
|
|
||||||
public void AsyncTerminate() |
|
||||||
{ |
|
||||||
// Resume stoped tread
|
|
||||||
if (this.IsPaused) { |
|
||||||
// We might get more callbacks so we should maintain consistent sate
|
|
||||||
//AsyncContinue(); // Continue the process to get remaining callbacks
|
|
||||||
} |
|
||||||
|
|
||||||
// Expose race condition - drain callback queue
|
|
||||||
System.Threading.Thread.Sleep(0); |
|
||||||
|
|
||||||
// Stop&terminate - both must be called
|
|
||||||
corProcess.Stop(uint.MaxValue); |
|
||||||
corProcess.Terminate(0); |
|
||||||
this.TerminateCommandIssued = true; |
|
||||||
|
|
||||||
// Do not mark the process as exited
|
|
||||||
// This is done once ExitProcess callback is received
|
|
||||||
} |
|
||||||
|
|
||||||
void SelectSomeThread() |
|
||||||
{ |
|
||||||
if (this.SelectedThread != null && !this.SelectedThread.IsInValidState) { |
|
||||||
this.SelectedThread = null; |
|
||||||
} |
|
||||||
if (this.SelectedThread == null) { |
|
||||||
foreach(Thread thread in this.Threads) { |
|
||||||
if (thread.IsInValidState) { |
|
||||||
this.SelectedThread = thread; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
internal void CheckSelectedStackFrames() |
|
||||||
{ |
|
||||||
foreach(Thread thread in this.Threads) { |
|
||||||
if (thread.IsInValidState) { |
|
||||||
if (thread.SelectedStackFrame != null && thread.SelectedStackFrame.IsInvalid) { |
|
||||||
thread.SelectedStackFrame = null; |
|
||||||
} |
|
||||||
} else { |
|
||||||
thread.SelectedStackFrame = null; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
internal void SelectMostRecentStackFrameWithLoadedSymbols() |
|
||||||
{ |
|
||||||
SelectSomeThread(); |
|
||||||
if (this.SelectedThread != null) { |
|
||||||
this.SelectedThread.SelectedStackFrame = this.SelectedThread.MostRecentStackFrameWithLoadedSymbols; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
internal void DisableAllSteppers() |
|
||||||
{ |
|
||||||
foreach(Thread thread in this.Threads) { |
|
||||||
thread.CurrentStepIn = null; |
|
||||||
foreach(Stepper stepper in thread.Steppers) { |
|
||||||
stepper.Ignore = true; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Waits until the debugger pauses unless it is already paused.
|
|
||||||
/// Use PausedReason to find out why it paused.
|
|
||||||
/// </summary>
|
|
||||||
public void WaitForPause() |
|
||||||
{ |
|
||||||
while(this.IsRunning && !this.HasExited) { |
|
||||||
debugger.MTA2STA.WaitForCall(); |
|
||||||
debugger.MTA2STA.PerformAllCalls(); |
|
||||||
} |
|
||||||
if (this.HasExited) throw new ProcessExitedException(); |
|
||||||
} |
|
||||||
|
|
||||||
public void WaitForPause(TimeSpan timeout) |
|
||||||
{ |
|
||||||
DateTime endTime = Util.HighPrecisionTimer.Now + timeout; |
|
||||||
while(this.IsRunning && !this.HasExited) { |
|
||||||
TimeSpan timeLeft = endTime - Util.HighPrecisionTimer.Now; |
|
||||||
if (timeLeft <= TimeSpan.FromMilliseconds(10)) break; |
|
||||||
//this.TraceMessage("Time left: " + timeLeft.TotalMilliseconds);
|
|
||||||
debugger.MTA2STA.WaitForCall(timeLeft); |
|
||||||
debugger.MTA2STA.PerformCall(); |
|
||||||
} |
|
||||||
if (this.HasExited) throw new ProcessExitedException(); |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Waits until the precesses exits.
|
|
||||||
/// </summary>
|
|
||||||
public void WaitForExit() |
|
||||||
{ |
|
||||||
while(!this.HasExited) { |
|
||||||
debugger.MTA2STA.WaitForCall(); |
|
||||||
debugger.MTA2STA.PerformAllCalls(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,28 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
|
||||||
|
using Debugger.Wrappers.CorDebug; |
||||||
|
|
||||||
|
namespace Debugger |
||||||
|
{ |
||||||
|
[Serializable] |
||||||
|
public class ProcessEventArgs: DebuggerEventArgs |
||||||
|
{ |
||||||
|
Process process; |
||||||
|
|
||||||
|
public Process Process { |
||||||
|
get { return process; } |
||||||
|
} |
||||||
|
|
||||||
|
public ProcessEventArgs(Process process): base(process == null ? null : process.Debugger) |
||||||
|
{ |
||||||
|
this.process = process; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,52 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using ICSharpCode.NRefactory.Ast; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using Debugger.MetaData; |
||||||
|
using Debugger.Wrappers.CorDebug; |
||||||
|
|
||||||
|
namespace Debugger |
||||||
|
{ |
||||||
|
public class GetValueException: DebuggerException |
||||||
|
{ |
||||||
|
INode expression; |
||||||
|
string error; |
||||||
|
|
||||||
|
/// <summary> Expression that has caused this exception to occur </summary>
|
||||||
|
public INode Expression { |
||||||
|
get { return expression; } |
||||||
|
set { expression = value; } |
||||||
|
} |
||||||
|
|
||||||
|
public string Error { |
||||||
|
get { return error; } |
||||||
|
} |
||||||
|
|
||||||
|
public override string Message { |
||||||
|
get { |
||||||
|
if (expression == null) { |
||||||
|
return error; |
||||||
|
} else { |
||||||
|
return String.Format("Error evaluating \"{0}\": {1}", expression.PrettyPrint(), error); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public GetValueException(INode expression, string error):base(error) |
||||||
|
{ |
||||||
|
this.expression = expression; |
||||||
|
this.error = error; |
||||||
|
} |
||||||
|
|
||||||
|
public GetValueException(string error):base(error) |
||||||
|
{ |
||||||
|
this.error = error; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -1,115 +0,0 @@ |
|||||||
// <file>
|
|
||||||
// <copyright see="prj:///doc/copyright.txt"/>
|
|
||||||
// <license see="prj:///doc/license.txt"/>
|
|
||||||
// <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
|
|
||||||
// <version>$Revision$</version>
|
|
||||||
// </file>
|
|
||||||
|
|
||||||
using ICSharpCode.NRefactory.Ast; |
|
||||||
using System; |
|
||||||
using System.Collections.Generic; |
|
||||||
using Debugger.Wrappers.CorDebug; |
|
||||||
|
|
||||||
// TODO: Test non-zero LowerBound
|
|
||||||
// TODO: Test very large arrays (Length > Int32.MaxValue)
|
|
||||||
|
|
||||||
namespace Debugger |
|
||||||
{ |
|
||||||
// This part of the class provides support for arrays
|
|
||||||
public partial class Value |
|
||||||
{ |
|
||||||
ICorDebugArrayValue CorArrayValue { |
|
||||||
get { |
|
||||||
if (IsNull) throw new GetValueException("Value is null"); |
|
||||||
if (!this.Type.IsArray) throw new DebuggerException("Value is not an array"); |
|
||||||
|
|
||||||
return this.CorReferenceValue.Dereference().CastTo<ICorDebugArrayValue>(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the number of elements in the array.
|
|
||||||
/// eg new object[4,5] returns 20
|
|
||||||
/// </summary>
|
|
||||||
/// <returns> 0 for non-arrays </returns>
|
|
||||||
public int ArrayLength { |
|
||||||
get { |
|
||||||
if (!this.Type.IsArray) return 0; |
|
||||||
return (int)CorArrayValue.Count; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the number of dimensions of the array.
|
|
||||||
/// eg new object[4,5] returns 2
|
|
||||||
/// </summary>
|
|
||||||
/// <returns> 0 for non-arrays </returns>
|
|
||||||
public int ArrayRank { |
|
||||||
get { |
|
||||||
if (!this.Type.IsArray) return 0; |
|
||||||
return (int)CorArrayValue.Rank; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary> Gets the dimensions of the array </summary>
|
|
||||||
/// <returns> null for non-arrays </returns>
|
|
||||||
public ArrayDimensions ArrayDimensions { |
|
||||||
get { |
|
||||||
if (!this.Type.IsArray) return null; |
|
||||||
int rank = this.ArrayRank; |
|
||||||
uint[] baseIndicies; |
|
||||||
if (CorArrayValue.HasBaseIndicies() == 1) { |
|
||||||
baseIndicies = CorArrayValue.BaseIndicies; |
|
||||||
} else { |
|
||||||
baseIndicies = new uint[this.ArrayRank]; |
|
||||||
} |
|
||||||
uint[] dimensionCounts = CorArrayValue.Dimensions; |
|
||||||
|
|
||||||
List<ArrayDimension> dimensions = new List<ArrayDimension>(); |
|
||||||
for(int i = 0; i < rank; i++) { |
|
||||||
dimensions.Add(new ArrayDimension((int)baseIndicies[i], (int)baseIndicies[i] + (int)dimensionCounts[i] - 1)); |
|
||||||
} |
|
||||||
|
|
||||||
return new ArrayDimensions(dimensions); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary> Returns an element of a single-dimensional array </summary>
|
|
||||||
public Value GetArrayElement(int index) |
|
||||||
{ |
|
||||||
return GetArrayElement(new int[] {index}); |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary> Returns an element of an array </summary>
|
|
||||||
public Value GetArrayElement(int[] elementIndices) |
|
||||||
{ |
|
||||||
int[] indices = (int[])elementIndices.Clone(); |
|
||||||
|
|
||||||
return new Value(this.AppDomain, GetCorValueOfArrayElement(indices)); |
|
||||||
} |
|
||||||
|
|
||||||
// May be called later
|
|
||||||
ICorDebugValue GetCorValueOfArrayElement(int[] indices) |
|
||||||
{ |
|
||||||
if (indices.Length != ArrayRank) { |
|
||||||
throw new GetValueException("Given indicies do not have the same dimension as array."); |
|
||||||
} |
|
||||||
if (!this.ArrayDimensions.IsIndexValid(indices)) { |
|
||||||
throw new GetValueException("Given indices are out of range of the array"); |
|
||||||
} |
|
||||||
|
|
||||||
return CorArrayValue.GetElement(indices); |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary> Returns all elements in the array </summary>
|
|
||||||
public Value[] GetArrayElements() |
|
||||||
{ |
|
||||||
if (!this.Type.IsArray) return null; |
|
||||||
List<Value> values = new List<Value>(); |
|
||||||
foreach(int[] indices in this.ArrayDimensions.Indices) { |
|
||||||
values.Add(GetArrayElement(indices)); |
|
||||||
} |
|
||||||
return values.ToArray(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,262 +0,0 @@ |
|||||||
// <file>
|
|
||||||
// <copyright see="prj:///doc/copyright.txt"/>
|
|
||||||
// <license see="prj:///doc/license.txt"/>
|
|
||||||
// <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
|
|
||||||
// <version>$Revision$</version>
|
|
||||||
// </file>
|
|
||||||
|
|
||||||
using System; |
|
||||||
using System.Collections.Generic; |
|
||||||
using Debugger.MetaData; |
|
||||||
using Debugger.Wrappers.CorDebug; |
|
||||||
using ICSharpCode.NRefactory.Ast; |
|
||||||
using System.Reflection; |
|
||||||
|
|
||||||
namespace Debugger |
|
||||||
{ |
|
||||||
// This part of the class provides support for classes and structures
|
|
||||||
public partial class Value |
|
||||||
{ |
|
||||||
internal ICorDebugObjectValue CorObjectValue { |
|
||||||
get { |
|
||||||
if (IsNull) throw new GetValueException("Value is null"); |
|
||||||
|
|
||||||
ICorDebugValue corValue = this.CorValue; |
|
||||||
// Dereference and unbox if necessary
|
|
||||||
if (corValue.Is<ICorDebugReferenceValue>()) |
|
||||||
corValue = corValue.CastTo<ICorDebugReferenceValue>().Dereference(); |
|
||||||
if (corValue.Is<ICorDebugBoxValue>()) |
|
||||||
return corValue.CastTo<ICorDebugBoxValue>().Object; |
|
||||||
if (!corValue.Is<ICorDebugObjectValue>()) |
|
||||||
throw new DebuggerException("Value is not an object"); |
|
||||||
return corValue.CastTo<ICorDebugObjectValue>(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void CheckObject(Value objectInstance, IDebugMemberInfo memberInfo) |
|
||||||
{ |
|
||||||
if (!memberInfo.IsStatic) { |
|
||||||
if (objectInstance == null) { |
|
||||||
throw new DebuggerException("No target object specified"); |
|
||||||
} |
|
||||||
if (objectInstance.IsNull) { |
|
||||||
throw new GetValueException("Null reference"); |
|
||||||
} |
|
||||||
//if (!objectInstance.IsObject) // eg Array.Length can be called
|
|
||||||
if (!memberInfo.DeclaringType.IsInstanceOfType(objectInstance)) { |
|
||||||
throw new GetValueException("Object is not of type " + memberInfo.DeclaringType.FullName); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#region Convenience overload methods
|
|
||||||
|
|
||||||
/// <summary> Get the value of given member. </summary>
|
|
||||||
public Value GetMemberValue(MemberInfo memberInfo, params Value[] arguments) |
|
||||||
{ |
|
||||||
return GetMemberValue(this, memberInfo, arguments); |
|
||||||
} |
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
/// <summary> Get the value of given member. </summary>
|
|
||||||
/// <param name="objectInstance">null if member is static</param>
|
|
||||||
public static Value GetMemberValue(Value objectInstance, MemberInfo memberInfo, params Value[] arguments) |
|
||||||
{ |
|
||||||
if (memberInfo is DebugFieldInfo) { |
|
||||||
if (arguments.Length > 0) throw new GetValueException("Arguments can not be used for a field"); |
|
||||||
return GetFieldValue(objectInstance, (DebugFieldInfo)memberInfo); |
|
||||||
} else if (memberInfo is DebugPropertyInfo) { |
|
||||||
return GetPropertyValue(objectInstance, (DebugPropertyInfo)memberInfo, arguments); |
|
||||||
} else if (memberInfo is DebugMethodInfo) { |
|
||||||
return InvokeMethod(objectInstance, (DebugMethodInfo)memberInfo, arguments); |
|
||||||
} |
|
||||||
throw new DebuggerException("Unknown member type: " + memberInfo.GetType()); |
|
||||||
} |
|
||||||
|
|
||||||
#region Convenience overload methods
|
|
||||||
|
|
||||||
/// <summary> Get the value of given field. </summary>
|
|
||||||
public Value GetFieldValue(DebugFieldInfo fieldInfo) |
|
||||||
{ |
|
||||||
return Value.GetFieldValue(this, fieldInfo); |
|
||||||
} |
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
/// <summary> Get the value of given field. </summary>
|
|
||||||
/// <param name="objectInstance">null if field is static</param>
|
|
||||||
public static Value GetFieldValue(Value objectInstance, DebugFieldInfo fieldInfo) |
|
||||||
{ |
|
||||||
return new Value( |
|
||||||
fieldInfo.AppDomain, |
|
||||||
GetFieldCorValue(objectInstance, fieldInfo) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
public static Value SetFieldValue(Value objectInstance, DebugFieldInfo fieldInfo, Value newValue) |
|
||||||
{ |
|
||||||
// TODO
|
|
||||||
throw new NotImplementedException(); |
|
||||||
} |
|
||||||
|
|
||||||
static ICorDebugValue GetFieldCorValue(Value objectInstance, DebugFieldInfo fieldInfo) |
|
||||||
{ |
|
||||||
CheckObject(objectInstance, fieldInfo); |
|
||||||
|
|
||||||
// Current frame is used to resolve context specific static values (eg. ThreadStatic)
|
|
||||||
ICorDebugFrame curFrame = null; |
|
||||||
if (fieldInfo.Process.IsPaused && |
|
||||||
fieldInfo.Process.SelectedThread != null && |
|
||||||
fieldInfo.Process.SelectedThread.MostRecentStackFrame != null && |
|
||||||
fieldInfo.Process.SelectedThread.MostRecentStackFrame.CorILFrame != null) { |
|
||||||
|
|
||||||
curFrame = fieldInfo.Process.SelectedThread.MostRecentStackFrame.CorILFrame.CastTo<ICorDebugFrame>(); |
|
||||||
} |
|
||||||
|
|
||||||
try { |
|
||||||
if (fieldInfo.IsStatic) { |
|
||||||
return ((DebugType)fieldInfo.DeclaringType).CorType.GetStaticFieldValue((uint)fieldInfo.MetadataToken, curFrame); |
|
||||||
} else { |
|
||||||
return objectInstance.CorObjectValue.GetFieldValue(((DebugType)fieldInfo.DeclaringType).CorType.Class, (uint)fieldInfo.MetadataToken); |
|
||||||
} |
|
||||||
} catch { |
|
||||||
throw new GetValueException("Can not get value of field"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#region Convenience overload methods
|
|
||||||
|
|
||||||
/// <summary> Get the value of the property using the get accessor </summary>
|
|
||||||
public Value GetPropertyValue(DebugPropertyInfo propertyInfo, params Value[] arguments) |
|
||||||
{ |
|
||||||
return GetPropertyValue(this, propertyInfo, arguments); |
|
||||||
} |
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
/// <summary> Get the value of the property using the get accessor </summary>
|
|
||||||
public static Value GetPropertyValue(Value objectInstance, DebugPropertyInfo propertyInfo, params Value[] arguments) |
|
||||||
{ |
|
||||||
CheckObject(objectInstance, propertyInfo); |
|
||||||
|
|
||||||
if (propertyInfo.GetGetMethod() == null) throw new GetValueException("Property does not have a get method"); |
|
||||||
|
|
||||||
Value val = Value.InvokeMethod(objectInstance, (DebugMethodInfo)propertyInfo.GetGetMethod(), arguments); |
|
||||||
|
|
||||||
return val; |
|
||||||
} |
|
||||||
|
|
||||||
#region Convenience overload methods
|
|
||||||
|
|
||||||
/// <summary> Set the value of the property using the set accessor </summary>
|
|
||||||
public Value SetPropertyValue(DebugPropertyInfo propertyInfo, Value newValue) |
|
||||||
{ |
|
||||||
return SetPropertyValue(this, propertyInfo, null, newValue); |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary> Set the value of the property using the set accessor </summary>
|
|
||||||
public Value SetPropertyValue(DebugPropertyInfo propertyInfo, Value[] arguments, Value newValue) |
|
||||||
{ |
|
||||||
return SetPropertyValue(this, propertyInfo, arguments, newValue); |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary> Set the value of the property using the set accessor </summary>
|
|
||||||
public static Value SetPropertyValue(Value objectInstance, DebugPropertyInfo propertyInfo, Value newValue) |
|
||||||
{ |
|
||||||
return SetPropertyValue(objectInstance, propertyInfo, null, newValue); |
|
||||||
} |
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
/// <summary> Set the value of the property using the set accessor </summary>
|
|
||||||
public static Value SetPropertyValue(Value objectInstance, DebugPropertyInfo propertyInfo, Value[] arguments, Value newValue) |
|
||||||
{ |
|
||||||
CheckObject(objectInstance, propertyInfo); |
|
||||||
|
|
||||||
if (propertyInfo.GetSetMethod() == null) throw new GetValueException("Property does not have a set method"); |
|
||||||
|
|
||||||
arguments = arguments ?? new Value[0]; |
|
||||||
|
|
||||||
Value[] allParams = new Value[1 + arguments.Length]; |
|
||||||
allParams[0] = newValue; |
|
||||||
arguments.CopyTo(allParams, 1); |
|
||||||
|
|
||||||
return Value.InvokeMethod(objectInstance, (DebugMethodInfo)propertyInfo.GetSetMethod(), allParams); |
|
||||||
} |
|
||||||
|
|
||||||
#region Convenience overload methods
|
|
||||||
|
|
||||||
/// <summary> Synchronously invoke the method </summary>
|
|
||||||
public Value InvokeMethod(DebugMethodInfo methodInfo, params Value[] arguments) |
|
||||||
{ |
|
||||||
return InvokeMethod(this, methodInfo, arguments); |
|
||||||
} |
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
/// <summary> Synchronously invoke the method </summary>
|
|
||||||
public static Value InvokeMethod(Value objectInstance, DebugMethodInfo methodInfo, params Value[] arguments) |
|
||||||
{ |
|
||||||
CheckObject(objectInstance, methodInfo); |
|
||||||
|
|
||||||
return Eval.InvokeMethod( |
|
||||||
methodInfo, |
|
||||||
methodInfo.IsStatic ? null : objectInstance, |
|
||||||
arguments ?? new Value[0] |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary> Invoke the ToString() method </summary>
|
|
||||||
public string InvokeToString() |
|
||||||
{ |
|
||||||
if (this.Type.IsPrimitive) return AsString; |
|
||||||
if (this.Type.IsPointer) return "0x" + this.PointerAddress.ToString("X"); |
|
||||||
// if (!IsObject) // Can invoke on primitives
|
|
||||||
DebugMethodInfo methodInfo = (DebugMethodInfo)DebugType.CreateFromType(this.AppDomain, typeof(object)).GetMethod("ToString", new DebugType[] {}); |
|
||||||
return Eval.InvokeMethod(methodInfo, this, new Value[] {}).AsString; |
|
||||||
} |
|
||||||
|
|
||||||
#region Convenience overload methods
|
|
||||||
|
|
||||||
/// <summary> Asynchronously invoke the method </summary>
|
|
||||||
public Eval AsyncInvokeMethod(DebugMethodInfo methodInfo, params Value[] arguments) |
|
||||||
{ |
|
||||||
return AsyncInvokeMethod(this, methodInfo, arguments); |
|
||||||
} |
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
/// <summary> Asynchronously invoke the method </summary>
|
|
||||||
public static Eval AsyncInvokeMethod(Value objectInstance, DebugMethodInfo methodInfo, params Value[] arguments) |
|
||||||
{ |
|
||||||
CheckObject(objectInstance, methodInfo); |
|
||||||
|
|
||||||
return Eval.AsyncInvokeMethod( |
|
||||||
methodInfo, |
|
||||||
methodInfo.IsStatic ? null : objectInstance, |
|
||||||
arguments ?? new Value[0] |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary> Get a field or property of an object with a given name. </summary>
|
|
||||||
/// <returns> Null if not found </returns>
|
|
||||||
public Value GetMemberValue(string name) |
|
||||||
{ |
|
||||||
DebugType currentType = this.Type; |
|
||||||
while (currentType != null) { |
|
||||||
MemberInfo memberInfo = currentType.GetMember<MemberInfo>(name, DebugType.BindingFlagsAll, null); |
|
||||||
if (memberInfo != null) { |
|
||||||
if (memberInfo is DebugFieldInfo) { |
|
||||||
return this.GetFieldValue((DebugFieldInfo)memberInfo); |
|
||||||
} |
|
||||||
if (memberInfo is DebugPropertyInfo) { |
|
||||||
return this.GetPropertyValue((DebugPropertyInfo)memberInfo); |
|
||||||
} |
|
||||||
} |
|
||||||
currentType = (DebugType)currentType.BaseType; |
|
||||||
} |
|
||||||
return null; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,66 +0,0 @@ |
|||||||
// <file>
|
|
||||||
// <copyright see="prj:///doc/copyright.txt"/>
|
|
||||||
// <license see="prj:///doc/license.txt"/>
|
|
||||||
// <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
|
|
||||||
// <version>$Revision$</version>
|
|
||||||
// </file>
|
|
||||||
|
|
||||||
using System; |
|
||||||
using System.ComponentModel; |
|
||||||
using Debugger.Wrappers.CorDebug; |
|
||||||
|
|
||||||
namespace Debugger |
|
||||||
{ |
|
||||||
// This part of the class provides support for primitive types
|
|
||||||
// eg int, bool, string
|
|
||||||
public partial class Value |
|
||||||
{ |
|
||||||
internal ICorDebugGenericValue CorGenericValue { |
|
||||||
get { |
|
||||||
if (IsNull) throw new GetValueException("Value is null"); |
|
||||||
|
|
||||||
ICorDebugValue corValue = this.CorValue; |
|
||||||
// Dereference and unbox if necessary
|
|
||||||
if (corValue.Is<ICorDebugReferenceValue>()) |
|
||||||
corValue = corValue.CastTo<ICorDebugReferenceValue>().Dereference(); |
|
||||||
if (corValue.Is<ICorDebugBoxValue>()) |
|
||||||
corValue = corValue.CastTo<ICorDebugBoxValue>().Object.CastTo<ICorDebugValue>(); |
|
||||||
if (!corValue.Is<ICorDebugGenericValue>()) |
|
||||||
throw new DebuggerException("Value is not an generic value"); |
|
||||||
return corValue.CastTo<ICorDebugGenericValue>(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the value of a primitive type.
|
|
||||||
///
|
|
||||||
/// If setting of a value fails, NotSupportedException is thrown.
|
|
||||||
/// </summary>
|
|
||||||
public object PrimitiveValue { |
|
||||||
get { |
|
||||||
if (this.Type.PrimitiveType == null) throw new DebuggerException("Value is not a primitive type"); |
|
||||||
if (this.Type.FullName == typeof(string).FullName) { |
|
||||||
if (this.IsNull) return null; |
|
||||||
return this.CorReferenceValue.Dereference().CastTo<ICorDebugStringValue>().String; |
|
||||||
} else { |
|
||||||
return CorGenericValue.GetValue(this.Type.PrimitiveType); |
|
||||||
} |
|
||||||
} |
|
||||||
set { |
|
||||||
if (this.Type.PrimitiveType == null) throw new DebuggerException("Value is not a primitive type"); |
|
||||||
if (this.Type.FullName == typeof(string).FullName) { |
|
||||||
this.SetValue(Eval.NewString(this.AppDomain, value.ToString())); |
|
||||||
} else { |
|
||||||
if (value == null) throw new DebuggerException("Can not set primitive value to null"); |
|
||||||
object newValue; |
|
||||||
try { |
|
||||||
newValue = Convert.ChangeType(value, this.Type.PrimitiveType); |
|
||||||
} catch { |
|
||||||
throw new NotSupportedException("Can not convert " + value.GetType().ToString() + " to " + this.Type.PrimitiveType.ToString()); |
|
||||||
} |
|
||||||
CorGenericValue.SetValue(this.Type.PrimitiveType, newValue); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
Loading…
Reference in new issue