-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #137 from Washi1337/feature/better-debug-views
Add debugger display proxies for various primitives in emulator
- Loading branch information
Showing
7 changed files
with
278 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
120 changes: 120 additions & 0 deletions
120
src/Platforms/Echo.Platforms.AsmResolver/Emulation/ObjectHandle.DebugProxy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using AsmResolver.DotNet.Signatures.Types; | ||
using AsmResolver.PE.DotNet.Metadata.Tables.Rows; | ||
|
||
namespace Echo.Platforms.AsmResolver.Emulation; | ||
|
||
[DebuggerTypeProxy(typeof(DebuggerProxy))] | ||
public readonly partial struct ObjectHandle | ||
{ | ||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] | ||
private object? Tag | ||
{ | ||
get | ||
{ | ||
if (IsNull) | ||
return null; | ||
|
||
try | ||
{ | ||
return GetObjectType(); | ||
} | ||
catch | ||
{ | ||
return "<invalid>"; | ||
} | ||
} | ||
} | ||
|
||
private sealed class DebuggerProxy | ||
{ | ||
private readonly ObjectHandle _handle; | ||
|
||
public DebuggerProxy(ObjectHandle handle) | ||
{ | ||
_handle = handle; | ||
} | ||
|
||
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] | ||
public KeyValuePair<object, object>[] Items | ||
{ | ||
get | ||
{ | ||
if (_handle.IsNull) | ||
return Array.Empty<KeyValuePair<object, object>>(); | ||
|
||
var type = _handle.GetObjectType().ToTypeSignature(); | ||
|
||
return type switch | ||
{ | ||
CorLibTypeSignature {ElementType: ElementType.String} => GetStringValue(), | ||
SzArrayTypeSignature arrayType => GetArrayValues(arrayType), | ||
_ => GetObjectFieldValues(type) | ||
}; | ||
} | ||
} | ||
|
||
private KeyValuePair<object, object>[] GetStringValue() | ||
{ | ||
// We cannot infer the string data if we don't know its length. | ||
if (!_handle.ReadStringLength().IsFullyKnown) | ||
return Array.Empty<KeyValuePair<object, object>>(); | ||
|
||
// Read and stringify the data. | ||
var data = _handle.ReadStringData(); | ||
char[] result = new char[data.ByteCount / 2]; | ||
for (int i = 0; i < result.Length; i++) | ||
{ | ||
var c = data.AsSpan(i * 8 * sizeof(char), 8 * sizeof(char)); | ||
result[i] = c.IsFullyKnown | ||
? (char) c.U16 | ||
: '?'; | ||
} | ||
|
||
return new[] {new KeyValuePair<object, object>(0, new string(result))}; | ||
} | ||
|
||
private KeyValuePair<object, object>[] GetArrayValues(SzArrayTypeSignature type) | ||
{ | ||
// We cannot infer the array data if we don't know its length. | ||
var length = _handle.ReadArrayLength(); | ||
if (!length.IsFullyKnown) | ||
return Array.Empty<KeyValuePair<object, object>>(); | ||
|
||
var elementType = type.BaseType; | ||
|
||
// Collect all elements. | ||
var result = new KeyValuePair<object, object>[length.AsSpan().I32]; | ||
for (int i = 0; i < result.Length; i++) | ||
{ | ||
var element = _handle.ReadArrayElement(elementType, i); | ||
result[i] = new KeyValuePair<object, object>(i, element); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
private KeyValuePair<object, object>[] GetObjectFieldValues(TypeSignature type) | ||
{ | ||
// We need to resolve the type to know which fields to include. | ||
var definition = type.Resolve(); | ||
if (definition is null) | ||
return Array.Empty<KeyValuePair<object, object>>(); | ||
|
||
// Collect all fields and their values. | ||
var result = new List<KeyValuePair<object, object>>(); | ||
for (int i = 0; i < definition.Fields.Count; i++) | ||
{ | ||
var field = definition.Fields[i]; | ||
if (field.IsStatic) | ||
continue; | ||
|
||
result.Add(new KeyValuePair<object, object>(field, _handle.ReadField(field))); | ||
} | ||
|
||
return result.ToArray(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 74 additions & 0 deletions
74
src/Platforms/Echo.Platforms.AsmResolver/Emulation/Stack/CallFrame.DebugProxy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using AsmResolver.DotNet; | ||
using AsmResolver.DotNet.Code.Cil; | ||
using AsmResolver.DotNet.Collections; | ||
|
||
namespace Echo.Platforms.AsmResolver.Emulation.Stack; | ||
|
||
[DebuggerTypeProxy(typeof(DebuggerProxy))] | ||
public partial class CallFrame | ||
{ | ||
private sealed class DebuggerProxy | ||
{ | ||
private readonly CallFrame _frame; | ||
|
||
public DebuggerProxy(CallFrame frame) | ||
{ | ||
_frame = frame; | ||
} | ||
|
||
public IMethodDescriptor Method => _frame.Method; | ||
|
||
public int ProgramCounter => _frame.ProgramCounter; | ||
|
||
public EvaluationStack EvaluationStack => _frame.EvaluationStack; | ||
|
||
public ExceptionHandlerStack ExceptionHandlerStack => _frame.ExceptionHandlerStack; | ||
|
||
public KeyValuePair<Parameter, object>[] Arguments | ||
{ | ||
get | ||
{ | ||
// Verify we have access to everything. | ||
if (_frame is not {Body.Owner.Parameters: { } parameters, Method.Signature: { } signature}) | ||
return Array.Empty<KeyValuePair<Parameter, object>>(); | ||
|
||
// Verify count. | ||
int count = signature.GetTotalParameterCount(); | ||
if (count == 0) | ||
return Array.Empty<KeyValuePair<Parameter, object>>(); | ||
|
||
// Read all their values. | ||
var result = new KeyValuePair<Parameter, object>[count]; | ||
for (int i = 0; i < result.Length; i++) | ||
{ | ||
result[i] = new KeyValuePair<Parameter, object>( | ||
parameters.GetBySignatureIndex(i), | ||
_frame.ReadArgument(i) | ||
); | ||
} | ||
|
||
return result; | ||
} | ||
} | ||
|
||
public KeyValuePair<CilLocalVariable, object>[] Locals | ||
{ | ||
get | ||
{ | ||
// Verify locals exist. | ||
if (_frame is not {Body.LocalVariables: {} locals} || locals.Count == 0) | ||
return Array.Empty<KeyValuePair<CilLocalVariable, object>>(); | ||
|
||
// Read all their values. | ||
var result = new KeyValuePair<CilLocalVariable, object>[locals.Count]; | ||
for (int i = 0; i < result.Length; i++) | ||
result[i] = new KeyValuePair<CilLocalVariable, object>(locals[i], _frame.ReadLocal(i)); | ||
|
||
return result; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.