//
//
//
//
// $Revision: 2285 $
//
using System;
using System.Collections.Generic;
using Debugger.Wrappers.CorDebug;
namespace Debugger
{
///
/// Delegate that is used to get value. This delegate may be called at any time and should never return null.
///
delegate ICorDebugValue CorValueGetter();
///
/// Value class holds data necessaty to obtain the value of a given object
/// even after continue. It provides functions to examine the object.
///
///
///
/// Expiration: Once value expires it can not be used anymore.
/// Expiration is permanet - once value expires it stays expired.
/// Value expires when any object specified in constructor expires
/// or when process exits.
///
///
/// Mutation: As long as any dependecy does not mutate the last
/// obteined value is still considered up to date. (If continue is
/// called and internal value is neutred, new copy will be obatined)
///
///
public partial class Value: DebuggerObject, IExpirable, IMutable
{
Process process;
CorValueGetter corValueGetter;
ICorDebugValue corValue;
PauseSession corValue_pauseSession;
/// Occurs when the Value can not be used anymore
public event EventHandler Expired;
/// Occurs when the Value have potentialy changed
public event EventHandler Changed;
bool isExpired = false;
ValueCache cache;
bool is_cnst=false;
private class ValueCache
{
public PauseSession PauseSession;
public ICorDebugValue RawCorValue;
public ICorDebugValue CorValue;
public DebugType Type;
public string AsString = String.Empty;
}
///
/// Cache stores expensive or commonly used information about the value
///
unsafe ValueCache Cache {
get {
if (this.HasExpired) throw new CannotGetValueException("Value has expired");
if (cache == null || (cache.PauseSession != process.PauseSession && !cache.RawCorValue.Is()) || is_cnst) {
DateTime startTime = Util.HighPrecisionTimer.Now;
is_cnst = false;
ValueCache newCache = new ValueCache();
newCache.RawCorValue = corValueGetter();
newCache.PauseSession = process.PauseSession;
newCache.CorValue = DereferenceUnbox(newCache.RawCorValue);
//newCache.Type = DebugType.Create(process, newCache.RawCorValue.As().ExactType);
//if (process.SelectedFunction != null)
//newCache.Type = DebugType.Create(process, newCache.RawCorValue.As().ExactType.Class,process.SelectedFunction.CorILFrame.CastTo().EnumerateTypeParameters().ToList().ToArray());
//else
newCache.Type = DebugType.Create(process, newCache.RawCorValue.As().ExactType);
cache = newCache;
// AsString representation
if (IsNull) cache.AsString = "";
if (IsArray) cache.AsString = "{" + this.Type.FullName + "}";
if (IsObject)
{
//if (!Type.IsValueType)
cache.AsString = "{" + this.Type.FullName + "}";
// else
// {
// cache.AsString = System.Runtime.InteropServices.Marshal.ReadInt32(newCache.CorValue.Address,0).ToString();
// }
}
//if (IsObject) cache.AsString = Eval.InvokeMethod(Process, typeof(object), "ToString", this, new Value[] {}).AsString;
// if (Type.IsByRef() && !IsPrimitive)
// newCache.CorValue = DereferenceUnbox(newCache.CorValue);
// else
if (Type.IsByRef() && (PrimitiveValue == null || PrimitiveValue.ToString() == "")) cache.AsString = "{}";
else
if (IsPrimitive || Type.IsByRef())
cache.AsString = PrimitiveValue != null ? PrimitiveValue.ToString() : String.Empty;
// else
{
//cache.AsString = System.Runtime.InteropServices.Marshal.ReadInt32(newCache.RawCorValue.Address,0).ToString();
// IntPtr buf = System.Runtime.InteropServices.Marshal.AllocHGlobal((int)newCache.RawCorValue.Size);
// this.process.CorProcess.ReadMemory(newCache.RawCorValue.Address,newCache.RawCorValue.Size,buf);
// int ptr = System.Runtime.InteropServices.Marshal.ReadInt32(buf);
// cache.AsString = ptr.ToString();
// cache.AsString = "{" + this.Type.FullName + "}";
}
//else
if (Type.IsPointer) cache.AsString = "{" + this.Type.FullName + "}";
//if (Type.IsPointer) cache.AsString = System.Runtime.InteropServices.Marshal.ReadInt32(newCache.CorValue.Address,0).ToString();
/*if (Type.IsPointer)
{
IntPtr buf = System.Runtime.InteropServices.Marshal.AllocHGlobal((int)newCache.CorValue.Size);
this.process.CorProcess.ReadMemory(newCache.CorValue.Address,newCache.CorValue.Size,buf);
int ptr = System.Runtime.InteropServices.Marshal.ReadInt32(buf);
cache.AsString = ptr.ToString();
}*/
TimeSpan totalTime = Util.HighPrecisionTimer.Now - startTime;
string name = this is NamedValue ? ((NamedValue)this).Name + " = " : String.Empty;
process.TraceMessage("Obtained value: " + name + cache.AsString + " (" + totalTime.TotalMilliseconds + " ms)");
}
return cache;
}
}
public Value GetPermanentReference()
{
ICorDebugValue corValue = this.CorValue;
if (this.Type.IsClass) {
corValue = this.CorObjectValue.CastTo().CreateHandle(CorDebugHandleType.HANDLE_STRONG).CastTo();
}
if (this.Type.IsValueType || this.Type.IsPrimitive) {
if (!corValue.Is()) {
// Box the value type
if (this.Type.IsPrimitive) {
// Get value type for the primive type
object o = this.PrimitiveValue;
corValue = Eval.NewObjectNoConstructor(DebugType.Create(process, null, this.Type.FullName)).RawCorValue;
this.SetPrimitiveValue(o);
} else {
corValue = Eval.NewObjectNoConstructor(this.Type).RawCorValue;
}
// Make the reference to box permanent
corValue = corValue.CastTo().Dereference().CastTo().CreateHandle(CorDebugHandleType.HANDLE_STRONG).CastTo();
// Create new value
Value newValue = new Value(process, new IExpirable[] {},
new IMutable[] {}, delegate {return corValue;});
// Copy the data inside the box
newValue.CorGenericValue.RawValue = this.CorGenericValue.RawValue;
return newValue;
} else {
// Make the reference to box permanent
corValue = corValue.CastTo().Dereference().CastTo().CreateHandle(CorDebugHandleType.HANDLE_STRONG).CastTo();
}
}
return new Value(process, new IExpirable[] {},
new IMutable[] {}, delegate {return corValue;});
}
/// The process that owns the value
[Debugger.Tests.Ignore]
public Process Process {
get {
return process;
}
}
private bool noEvaled;
public bool NoEvaled
{
get
{
return noEvaled;
}
set
{
noEvaled = value;
}
}
public void SetPrimitiveValue(object val)
{
if (CorType == CorElementType.STRING) {
throw new NotSupportedException();
} else {
CorGenericValue.Value = val;
}
this.cache.AsString = val.ToString();
}
/// Returns true if the Value have expired
/// and can not be used anymore
public bool HasExpired {
get {
return isExpired;
}
}
// public Value GetPermanentReference()
// {
// return new Value(this.process,new IExpirable[] {},
// new IMutable[] {}, delegate { return DereferenceUnbox(this.RawCorValue).CastTo().CreateHandle(CorDebugHandleType.HANDLE_STRONG).CastTo();});
// }
public bool IsReferenceEqual(Value v)
{
if (IsNull && v.IsNull) return true;
if (v.IsNull) return false;
return this.CorValue.Address == v.CorValue.Address;
}
internal ICorDebugValue RawCorValue {
get {
return Cache.RawCorValue;
}
}
internal ICorDebugValue CorValue {
get {
return Cache.CorValue;
}
}
// internal ICorDebugValue CorValue {
// get {
// return this.corValue;
// }
// }
internal CorElementType CorType {
get {
ICorDebugValue corValue = this.CorValue;
if (corValue == null) {
return (CorElementType)0;
}
return (CorElementType)corValue.Type;
}
}
internal ICorDebugValue SoftReference {
get {
if (this.HasExpired) throw new DebuggerException("CorValue has expired");
ICorDebugValue corValue = RawCorValue;
if (corValue != null && corValue.Is()) {
return corValue;
}
corValue = DereferenceUnbox(corValue);
if (corValue != null && corValue.Is()) {
return corValue.As().CreateHandle(CorDebugHandleType.HANDLE_WEAK_TRACK_RESURRECTION).CastTo();
} else {
return corValue; // Value type - return value type
}
}
}
public Value Dereference
{
get
{
Value v = new Value(this.process,new IExpirable[] {},
new IMutable[] {},delegate {return this.SoftReference;}, true);
return v;
}
}
private bool is_deref;
internal Value(Process process,
IExpirable[] expireDependencies,
IMutable[] mutateDependencies,
CorValueGetter corValueGetter, bool is_deref)
{
this.process = process;
AddExpireDependency(process);
foreach(IExpirable exp in expireDependencies) {
AddExpireDependency(exp);
}
foreach(IMutable mut in mutateDependencies) {
AddMutateDependency(mut);
}
this.corValueGetter = corValueGetter;
this.is_deref = is_deref;
}
internal Value(Process process,
IExpirable[] expireDependencies,
IMutable[] mutateDependencies,
CorValueGetter corValueGetter)
{
this.process = process;
AddExpireDependency(process);
foreach(IExpirable exp in expireDependencies) {
AddExpireDependency(exp);
}
foreach(IMutable mut in mutateDependencies) {
AddMutateDependency(mut);
}
this.corValueGetter = corValueGetter;
}
void AddExpireDependency(IExpirable dependency)
{
if (dependency.HasExpired) {
MakeExpired();
} else {
dependency.Expired += delegate { MakeExpired(); };
}
}
void MakeExpired()
{
if (!isExpired) {
isExpired = true;
OnExpired(new ValueEventArgs(this));
}
}
void AddMutateDependency(IMutable dependency)
{
dependency.Changed += delegate { NotifyChange(); };
}
internal void NotifyChange()
{
cache = null;
if (!isExpired) {
OnChanged(new ValueEventArgs(this));
}
}
/// Is called when the value changes
protected virtual void OnChanged(ProcessEventArgs e)
{
if (Changed != null) {
Changed(this, e);
}
}
/// Is called when the value expires and can not be
/// used anymore
protected virtual void OnExpired(EventArgs e)
{
if (Expired != null) {
Expired(this, e);
}
}
/// Returns the of the value
[Debugger.Tests.SummaryOnly]
public DebugType Type {
get {
return Cache.Type;
}
}
public void SetValue(Value newValue)
{
ICorDebugValue corValue = this.CorValue;
ICorDebugValue newCorValue = newValue.CorValue;
if (corValue.Is()) {
if (newCorValue.Is()) {
ICorDebugValue box = Eval.NewObjectNoConstructor(newValue.Type).CorValue;
newCorValue = box;
}
corValue.CastTo().SetValue(newCorValue.CastTo().Value);
} else {
corValue.CastTo().RawValue =
newCorValue.CastTo().RawValue;
}
}
}
public class CannotGetValueException: DebuggerException
{
public CannotGetValueException(string message):base(message)
{
}
}
public class TooLongEvaluation : DebuggerException
{
public TooLongEvaluation()
{
}
}
}