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] Added support for getting members of static structures. #69542

Merged
merged 13 commits into from
Jul 28, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ public async Task<JObject> ReadAsVariableValue(
CancellationToken token,
bool isOwn = false,
int typeIdForObject = -1,
bool forDebuggerDisplayAttribute = false)
bool forDebuggerDisplayAttribute = false,
bool includeStatic = false)
{
long initialPos = /*retDebuggerCmdReader == null ? 0 : */retDebuggerCmdReader.BaseStream.Position;
ElementType etype = (ElementType)retDebuggerCmdReader.ReadByte();
Expand Down Expand Up @@ -199,7 +200,7 @@ public async Task<JObject> ReadAsVariableValue(
}
case ElementType.ValueType:
{
ret = await ReadAsValueType(retDebuggerCmdReader, name, initialPos, forDebuggerDisplayAttribute, token);
ret = await ReadAsValueType(retDebuggerCmdReader, name, initialPos, forDebuggerDisplayAttribute, includeStatic, token);
break;
}
case (ElementType)ValueTypeId.Null:
Expand Down Expand Up @@ -300,6 +301,7 @@ public async Task<JObject> ReadAsValueType(
string name,
long initialPos,
bool forDebuggerDisplayAttribute,
bool includeStatic,
CancellationToken token)
{
// FIXME: debugger proxy
Expand Down Expand Up @@ -337,6 +339,7 @@ public async Task<JObject> ReadAsValueType(
typeId,
numValues,
isEnum,
includeStatic,
token);
_valueTypes[valueType.Id.Value] = valueType;
return await valueType.ToJObject(_sdbAgent, forDebuggerDisplayAttribute, token);
Expand Down
23 changes: 13 additions & 10 deletions src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ public static async Task<Dictionary<string, JObject>> ExpandPropertyValues(
bool isOwn,
CancellationToken token,
Dictionary<string, JObject> allMembers,
bool includeStatic = false)
bool includeStatic)
{
using var retDebuggerCmdReader = await sdbHelper.GetTypePropertiesReader(typeId, token);
if (retDebuggerCmdReader == null)
Expand All @@ -323,7 +323,8 @@ public static async Task<Dictionary<string, JObject>> ExpandPropertyValues(
var attrs = (PropertyAttributes)retDebuggerCmdReader.ReadInt32(); //attrs
if (getMethodId == 0 || await sdbHelper.GetParamCount(getMethodId, token) != 0)
continue;
if (!includeStatic && await sdbHelper.MethodIsStatic(getMethodId, token))
bool isStatic = await sdbHelper.MethodIsStatic(getMethodId, token);
if (!includeStatic && isStatic)
continue;

MethodInfoWithDebugInformation getterInfo = await sdbHelper.GetMethodInfo(getMethodId, token);
Expand All @@ -336,7 +337,7 @@ public static async Task<Dictionary<string, JObject>> ExpandPropertyValues(
if (!allMembers.TryGetValue(propName, out JObject existingMember))
{
// new member
await AddProperty(getMethodId, state, propName, getterMemberAccessAttrs);
await AddProperty(getMethodId, state, propName, getterMemberAccessAttrs, isStatic);
continue;
}

Expand Down Expand Up @@ -387,7 +388,7 @@ public static async Task<Dictionary<string, JObject>> ExpandPropertyValues(
{
// hiding with a non-auto property, so nothing to adjust
// add the new property
await AddProperty(getMethodId, state, overriddenOrHiddenPropName, getterMemberAccessAttrs);
await AddProperty(getMethodId, state, overriddenOrHiddenPropName, getterMemberAccessAttrs, isStatic);
continue;
}

Expand Down Expand Up @@ -419,7 +420,7 @@ async Task UpdateBackingFieldWithPropertyAttributes(JObject backingField, string
allMembers[evalue["name"].Value<string>()] = evalue;
}

async Task AddProperty(int getMethodId, DebuggerBrowsableState? state, string propNameWithSufix, MethodAttributes getterAttrs)
async Task AddProperty(int getMethodId, DebuggerBrowsableState? state, string propNameWithSufix, MethodAttributes getterAttrs, bool isPropertyStatic)
{
string returnTypeName = await sdbHelper.GetReturnType(getMethodId, token);
JObject propRet = null;
Expand All @@ -431,12 +432,12 @@ async Task AddProperty(int getMethodId, DebuggerBrowsableState? state, string pr
}
catch (Exception)
{
propRet = GetNotAutoExpandableObject(getMethodId, propNameWithSufix);
propRet = GetNotAutoExpandableObject(getMethodId, propNameWithSufix, isPropertyStatic);
}
}
else
{
propRet = GetNotAutoExpandableObject(getMethodId, propNameWithSufix);
propRet = GetNotAutoExpandableObject(getMethodId, propNameWithSufix, isPropertyStatic);
}

propRet["isOwn"] = isOwn;
Expand All @@ -461,11 +462,12 @@ async Task AddProperty(int getMethodId, DebuggerBrowsableState? state, string pr
}
}

JObject GetNotAutoExpandableObject(int methodId, string propertyName)
JObject GetNotAutoExpandableObject(int methodId, string propertyName, bool isStatic)
{
JObject methodIdArgs = JObject.FromObject(new
{
containerId = objectId.Value,
isStatic = isStatic,
containerId = isStatic ? typeId : objectId.Value,
isValueType = isValueType,
methodId = methodId
});
Expand Down Expand Up @@ -563,7 +565,8 @@ public static async Task<GetMembersResult> GetObjectMemberValues(
isValueType: false,
isOwn,
token,
allMembers);
allMembers,
includeStatic);

// ownProperties
// Note: ownProperties should mean that we return members of the klass itself,
Expand Down
2 changes: 1 addition & 1 deletion src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@ internal async Task<ValueOrError<GetMembersResult>> RuntimeGetObjectMembers(Sess
: ValueOrError<GetMembersResult>.WithError(resScope);
case "valuetype":
var resValue = await MemberObjectsExplorer.GetValueTypeMemberValues(
context.SdbAgent, objectId.Value, getObjectOptions, token, sortByAccessLevel, includeStatic: false);
context.SdbAgent, objectId.Value, getObjectOptions, token, sortByAccessLevel, includeStatic: true);
return resValue switch
{
null => ValueOrError<GetMembersResult>.WithError($"Could not get properties for {objectId}"),
Expand Down
23 changes: 13 additions & 10 deletions src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1772,20 +1772,18 @@ public async Task<JObject> InvokeMethod(ArraySegment<byte> argsBuffer, int metho
return await ValueCreator.ReadAsVariableValue(retDebuggerCmdReader, name, token);
}

public Task<JObject> InvokeMethod(int objectId, int methodId, bool isValueType, CancellationToken token)
public Task<JObject> InvokeMethod(int objectId, int methodId, bool isValueType, CancellationToken token, bool isMethodStatic = false)
{
if (isValueType)
if (isValueType && !isMethodStatic)
{
return ValueCreator.TryGetValueTypeById(objectId, out var valueType)
? InvokeMethod(valueType.Buffer, methodId, token)
: throw new ArgumentException($"Could not find valuetype with id {objectId}, for method id: {methodId}", nameof(objectId));
}
else
{
using var commandParamsObjWriter = new MonoBinaryWriter();
using var commandParamsObjWriter = new MonoBinaryWriter();
if (!isMethodStatic)
commandParamsObjWriter.Write(ElementType.Class, objectId);
return InvokeMethod(commandParamsObjWriter.GetParameterBuffer(), methodId, token);
}
return InvokeMethod(commandParamsObjWriter.GetParameterBuffer(), methodId, token);
}

public Task<JObject> InvokeMethod(DotnetObjectId dotnetObjectId, CancellationToken token, int methodId = -1)
Expand All @@ -1795,10 +1793,15 @@ public Task<JObject> InvokeMethod(DotnetObjectId dotnetObjectId, CancellationTok
JObject args = dotnetObjectId.ValueAsJson;
int? objectId = args["containerId"]?.Value<int>();
int? embeddedMethodId = args["methodId"]?.Value<int>();
bool isMethodStatic = args["isStatic"]?.Value<bool>() == true;

return objectId == null || embeddedMethodId == null
? throw new ArgumentException($"Invalid object id for a method, with missing container, or methodId", nameof(dotnetObjectId))
: InvokeMethod(objectId.Value, embeddedMethodId.Value, isValueType: args["isValueType"]?.Value<bool>() == true, token);
: InvokeMethod(objectId.Value,
embeddedMethodId.Value,
isValueType: args["isValueType"]?.Value<bool>() == true,
token,
isMethodStatic);
}

return dotnetObjectId.Scheme is "object" or "valuetype"
Expand Down Expand Up @@ -1941,7 +1944,7 @@ public async Task<JArray> StackFrameGetValues(MethodInfoWithDebugInformation met
using var retDebuggerCmdReader = await SendDebuggerAgentCommand(CmdFrame.GetThis, commandParamsWriter, token);
retDebuggerCmdReader.ReadByte(); //ignore type
var objectId = retDebuggerCmdReader.ReadInt32();
GetMembersResult asyncProxyMembers = await MemberObjectsExplorer.GetObjectMemberValues(this, objectId, GetObjectCommandOptions.WithProperties, token);
GetMembersResult asyncProxyMembers = await MemberObjectsExplorer.GetObjectMemberValues(this, objectId, GetObjectCommandOptions.WithProperties, token, includeStatic: true);
var asyncLocals = await GetHoistedLocalVariables(objectId, asyncProxyMembers.Flatten(), token);
return asyncLocals;
}
Expand All @@ -1952,7 +1955,7 @@ public async Task<JArray> StackFrameGetValues(MethodInfoWithDebugInformation met
{
try
{
var var_json = await ValueCreator.ReadAsVariableValue(localsDebuggerCmdReader, var.Name, token);
var var_json = await ValueCreator.ReadAsVariableValue(localsDebuggerCmdReader, var.Name, token, includeStatic: true);
locals.Add(var_json);
}
catch (Exception ex)
Expand Down
53 changes: 34 additions & 19 deletions src/mono/wasm/debugger/BrowserDebugProxy/ValueTypeClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,39 +53,35 @@ public static async Task<ValueTypeClass> CreateFromReader(
int typeId,
int numValues,
bool isEnum,
bool includeStatic,
CancellationToken token)
{
var typeInfo = await sdbAgent.GetTypeInfo(typeId, token);
var typeFieldsBrowsableInfo = typeInfo?.Info?.DebuggerBrowsableFields;
var typePropertiesBrowsableInfo = typeInfo?.Info?.DebuggerBrowsableProperties;

IReadOnlyList<FieldTypeClass> fieldTypes = await sdbAgent.GetTypeFields(typeId, token);
// statics should not be in valueType fields: CallFunctionOnTests.PropertyGettersTest

JArray fields = new();
if (includeStatic)
{
IEnumerable<FieldTypeClass> staticFields =
fieldTypes.Where(f => f.Attributes.HasFlag(FieldAttributes.Static));
foreach (var field in staticFields)
{
var fieldValue = await sdbAgent.GetFieldValue(typeId, field.Id, token);
fields.Add(GetFieldWithMetadata(field, fieldValue, isStatic: true));
}
}

IEnumerable<FieldTypeClass> writableFields = fieldTypes
.Where(f => !f.Attributes.HasFlag(FieldAttributes.Literal)
&& !f.Attributes.HasFlag(FieldAttributes.Static));

JArray fields = new();
foreach (var field in writableFields)
{
var fieldValue = await sdbAgent.ValueCreator.ReadAsVariableValue(cmdReader, field.Name, token, true, field.TypeId, false);

fieldValue["__section"] = field.Attributes switch
{
FieldAttributes.Private => "private",
FieldAttributes.Public => "result",
_ => "internal"
};

if (field.IsBackingField)
fieldValue["__isBackingField"] = true;
else
{
typeFieldsBrowsableInfo.TryGetValue(field.Name, out DebuggerBrowsableState? state);
fieldValue["__state"] = state?.ToString();
}

fields.Add(fieldValue);
fields.Add(GetFieldWithMetadata(field, fieldValue, isStatic: false));
}

long endPos = cmdReader.BaseStream.Position;
Expand All @@ -95,6 +91,25 @@ public static async Task<ValueTypeClass> CreateFromReader(
cmdReader.BaseStream.Position = endPos;

return new ValueTypeClass(valueTypeBuffer, className, fields, typeId, isEnum);

JObject GetFieldWithMetadata(FieldTypeClass field, JObject fieldValue, bool isStatic)
{
// GetFieldValue returns JObject without name and we need this information
if (isStatic)
fieldValue["name"] = field.Name;
fieldValue["__section"] = field.Attributes.HasFlag(FieldAttributes.Public)
? "public" :
field.Attributes.HasFlag(FieldAttributes.Assembly) ? "internal" : "private";

if (field.IsBackingField)
{
fieldValue["__isBackingField"] = true;
return fieldValue;
}
typeFieldsBrowsableInfo.TryGetValue(field.Name, out DebuggerBrowsableState? state);
fieldValue["__state"] = state?.ToString();
return fieldValue;
}
}

public async Task<JObject> ToJObject(MonoSDBHelper sdbAgent, bool forDebuggerDisplayAttribute, CancellationToken token)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,12 @@ public async Task PropertyGettersTest(string eval_fn, string method_name, int li

// Auto properties show w/o getters, because they have
// a backing field
DTAutoProperty = TDateTime(dt)
DTAutoProperty = TDateTime(dt),

// Static properties
PublicStaticDTProp = TGetter("PublicStaticDTProp"),
PrivateStaticDTProp = TGetter("PrivateStaticDTProp"),
InternalStaticDTProp = TGetter("InternalStaticDTProp"),
}, local_name);
ilonatommy marked this conversation as resolved.
Show resolved Hide resolved

// Invoke getters, and check values
Expand Down
Loading