Skip to content

Commit

Permalink
[wasm][debugger] Enable onCallFrame evaluation of static fields in cl…
Browse files Browse the repository at this point in the history
…asses/interfaces from a static method. (#70560)

* Test for DIM - expected behavior.

* Static tests: renaming variables and small refactoring.

* Added new testcases and merged existing tests into Theories.

* Fix for resolving the name with namespace prefix matching the current frame location.

* Fix for tests failing on InvokeMethod in runtime.

* Applied smallest suggestions of @radical's review.

* Reverted deleting and renaming tests - only adding new.

* Corrected the logic + added new tests.

* Removed unnecessary logging.
  • Loading branch information
ilonatommy authored Jul 8, 2022
1 parent fdb900e commit 0ab2c1e
Show file tree
Hide file tree
Showing 4 changed files with 304 additions and 77 deletions.
107 changes: 72 additions & 35 deletions src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,53 +77,73 @@ public async Task<JObject> GetValueFromObject(JToken objRet, CancellationToken t
return null;
}

public async Task<(JObject containerObject, ArraySegment<string> remaining)> ResolveStaticMembersInStaticTypes(ArraySegment<string> parts, CancellationToken token)
public async Task<(JObject containerObject, ArraySegment<string> remaining)> ResolveStaticMembersInStaticTypes(ArraySegment<string> expressionParts, CancellationToken token)
{
string classNameToFind = "";
var store = await proxy.LoadStore(sessionId, false, token);
var methodInfo = context.CallStack.FirstOrDefault(s => s.Id == scopeId)?.Method?.Info;

if (methodInfo == null)
return (null, null);

int typeId = -1;
for (int i = 0; i < parts.Count; i++)
string[] parts = expressionParts.ToArray();

string fullName = methodInfo.IsAsync == 0 ? methodInfo.TypeInfo.FullName : StripAsyncPartOfFullName(methodInfo.TypeInfo.FullName);
string[] fullNameParts = fullName.Split(".", StringSplitOptions.TrimEntries).ToArray();
for (int i = 0; i < fullNameParts.Length; i++)
{
string part = parts[i];
string[] fullNamePrefix = fullNameParts[..^i];
var (memberObject, remaining) = await FindStaticMemberMatchingParts(parts, fullNamePrefix);
if (memberObject != null)
return (memberObject, remaining);
}
return await FindStaticMemberMatchingParts(parts);

if (typeId != -1)
async Task<(JObject, ArraySegment<string>)> FindStaticMemberMatchingParts(string[] parts, string[] fullNameParts = null)
{
string classNameToFind = fullNameParts == null ? "" : string.Join(".", fullNameParts);
int typeId = -1;
for (int i = 0; i < parts.Length; i++)
{
JObject memberObject = await FindStaticMemberInType(classNameToFind, part, typeId);
if (memberObject != null)
if (!string.IsNullOrEmpty(methodInfo.TypeInfo.Namespace))
{
ArraySegment<string> remaining = null;
if (i < parts.Count - 1)
remaining = parts[i..];

return (memberObject, remaining);
typeId = await FindStaticTypeId(methodInfo.TypeInfo.Namespace + "." + classNameToFind);
if (typeId != -1)
continue;
}
typeId = await FindStaticTypeId(classNameToFind);

// Didn't find a member named `part` in `typeId`.
// Could be a nested type. Let's continue the search
// with `part` added to the type name
string part = parts[i];
if (typeId != -1)
{
JObject memberObject = await FindStaticMemberInType(classNameToFind, part, typeId);
if (memberObject != null)
{
ArraySegment<string> remaining = null;
if (i < parts.Length - 1)
remaining = parts[i..];
return (memberObject, remaining);
}

typeId = -1;
}
// Didn't find a member named `part` in `typeId`.
// Could be a nested type. Let's continue the search
// with `part` added to the type name

if (classNameToFind.Length > 0)
classNameToFind += ".";
classNameToFind += part;
typeId = -1;
}

if (!string.IsNullOrEmpty(methodInfo?.TypeInfo?.Namespace))
{
typeId = await FindStaticTypeId(methodInfo?.TypeInfo?.Namespace + "." + classNameToFind);
if (typeId != -1)
continue;
if (classNameToFind.Length > 0)
classNameToFind += ".";
classNameToFind += part;
}
typeId = await FindStaticTypeId(classNameToFind);
return (null, null);
}

return (null, null);
// async function full name has a form: namespaceName.<currentFrame'sMethodName>d__integer
static string StripAsyncPartOfFullName(string fullName)
=> fullName.IndexOf(".<") is int index && index < 0
? fullName
: fullName.Substring(0, index);


async Task<JObject> FindStaticMemberInType(string classNameToFind, string name, int typeId)
{
Expand All @@ -138,20 +158,37 @@ async Task<JObject> FindStaticMemberInType(string classNameToFind, string name,
{
isInitialized = await context.SdbAgent.TypeInitialize(typeId, token);
}
var staticFieldValue = await context.SdbAgent.GetFieldValue(typeId, field.Id, token);
var valueRet = await GetValueFromObject(staticFieldValue, token);
// we need the full name here
valueRet["className"] = classNameToFind;
return valueRet;
try
{
var staticFieldValue = await context.SdbAgent.GetFieldValue(typeId, field.Id, token);
var valueRet = await GetValueFromObject(staticFieldValue, token);
// we need the full name here
valueRet["className"] = classNameToFind;
return valueRet;
}
catch (Exception ex)
{
logger.LogDebug(ex, $"Failed to get value of field {field.Name} on {classNameToFind} " +
$"because {field.Name} is not a static member of {classNameToFind}.");
}
return null;
}

var methodId = await context.SdbAgent.GetPropertyMethodIdByName(typeId, name, token);
if (methodId != -1)
{
using var commandParamsObjWriter = new MonoBinaryWriter();
commandParamsObjWriter.Write(0); //param count
var retMethod = await context.SdbAgent.InvokeMethod(commandParamsObjWriter.GetParameterBuffer(), methodId, token);
return await GetValueFromObject(retMethod, token);
try
{
var retMethod = await context.SdbAgent.InvokeMethod(commandParamsObjWriter.GetParameterBuffer(), methodId, token);
return await GetValueFromObject(retMethod, token);
}
catch (Exception ex)
{
logger.LogDebug(ex, $"Failed to invoke getter of id={methodId} on {classNameToFind}.{name} " +
$"because {name} is not a static member of {classNameToFind}.");
}
}
return null;
}
Expand Down
9 changes: 9 additions & 0 deletions src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1416,6 +1416,15 @@ internal async Task SetJustMyCode(bool enabled)
Assert.True(res.IsOk);
Assert.Equal(res.Value["justMyCodeEnabled"], enabled);
}

internal async Task CheckEvaluateFail(string id, params (string expression, string message)[] args)
{
foreach (var arg in args)
{
(_, Result _res) = await EvaluateOnCallFrame(id, arg.expression, expect_ok: false).ConfigureAwait(false);;
AssertEqual(arg.message, _res.Error["result"]?["description"]?.Value<string>(), $"Expression '{arg.expression}' - wrong error message");
}
}
}

class DotnetObjectId
Expand Down
Loading

0 comments on commit 0ab2c1e

Please sign in to comment.