Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wasm][debugger] Enable onCallFrame evaluation of static fields in classes/interfaces from a static method. #70560

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ 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, bool includeFullName, CancellationToken token)
{
string classNameToFind = "";
var store = await proxy.LoadStore(sessionId, token);
Expand All @@ -86,8 +86,28 @@ public async Task<JObject> GetValueFromObject(JToken objRet, CancellationToken t
if (methodInfo == null)
return (null, null);

string[] parts;
if (includeFullName)
{
string fullName = methodInfo.IsAsync == 0 ? methodInfo.TypeInfo?.FullName : StripAsyncPartOfFullName(methodInfo.TypeInfo?.FullName);
ilonatommy marked this conversation as resolved.
Show resolved Hide resolved
string[] fullNameParts = fullName.Split(".", StringSplitOptions.TrimEntries);
int overlappingPartIdx = Enumerable.Range(0, fullNameParts.Length).LastOrDefault(i => fullNameParts[i] == expressionParts[0]);
ilonatommy marked this conversation as resolved.
Show resolved Hide resolved
ilonatommy marked this conversation as resolved.
Show resolved Hide resolved
if (overlappingPartIdx == 0)
ilonatommy marked this conversation as resolved.
Show resolved Hide resolved
{
// default val was returned for overlappingPartIdx
overlappingPartIdx = fullNameParts.Length;
}
parts = new string[fullNameParts[..overlappingPartIdx].Length + expressionParts.Count];
Array.Copy(fullNameParts[..overlappingPartIdx], parts, fullNameParts[..overlappingPartIdx].Length);
Array.Copy(expressionParts.Array, 0, parts, fullNameParts[..overlappingPartIdx].Length, expressionParts.Count);
ilonatommy marked this conversation as resolved.
Show resolved Hide resolved
}
else
{
parts = expressionParts.Array;
}

int typeId = -1;
for (int i = 0; i < parts.Count; i++)
for (int i = 0; i < parts.Length; i++)
{
string part = parts[i];

Expand All @@ -97,7 +117,7 @@ public async Task<JObject> GetValueFromObject(JToken objRet, CancellationToken t
if (memberObject != null)
{
ArraySegment<string> remaining = null;
if (i < parts.Count - 1)
if (i < parts.Length - 1)
remaining = parts[i..];

return (memberObject, remaining);
Expand All @@ -123,6 +143,12 @@ public async Task<JObject> GetValueFromObject(JToken objRet, CancellationToken t
typeId = await FindStaticTypeId(classNameToFind);
}

string StripAsyncPartOfFullName(string fullName)
{
// async function full name has a form: namespaceName.<currentFrame'sMethodName>d__integer
return fullName.Split(".<")[0];
}
ilonatommy marked this conversation as resolved.
Show resolved Hide resolved

return (null, null);

async Task<JObject> FindStaticMemberInType(string classNameToFind, string name, int typeId)
Expand All @@ -138,20 +164,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} " +
ilonatommy marked this conversation as resolved.
Show resolved Hide resolved
$"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 Expand Up @@ -201,7 +244,9 @@ public async Task<JObject> Resolve(string varName, CancellationToken token)

if (retObject == null)
{
(retObject, ArraySegment<string> remaining) = await ResolveStaticMembersInStaticTypes(parts, token);
(retObject, ArraySegment<string> remaining) = await ResolveStaticMembersInStaticTypes(parts, includeFullName: false, token);
if (retObject == null && remaining == null)
(retObject, remaining) = await ResolveStaticMembersInStaticTypes(parts, includeFullName: true, token);
if (remaining != null && remaining.Count != 0)
{
if (retObject.IsNullValuedObject())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1408,6 +1408,12 @@ internal async Task SetJustMyCode(bool enabled)
Assert.True(res.IsOk);
Assert.Equal(res.Value["justMyCodeEnabled"], enabled);
}

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

class DotnetObjectId
Expand Down
Loading