Skip to content

Commit

Permalink
[wasm][debugger] Added support for getting members of static structur…
Browse files Browse the repository at this point in the history
…es. (#69542)

* Added info flow about being static + including static.

* Added static testcases to Browsable, changed names to more suitable.

* Fixed tests affected by adding static members to GetProperties reply.

* Removed whitespaces I did not intend to correct.

* Test require full name after merge with main.

* Added @radical's sugestions.

* Fixed tests: Private attr is detected even when it's not set.

* Better way of checking attr flags.
  • Loading branch information
ilonatommy authored Jul 28, 2022
1 parent b31a48a commit b0766a9
Show file tree
Hide file tree
Showing 10 changed files with 649 additions and 82 deletions.
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 @@ -1797,20 +1797,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 @@ -1820,10 +1818,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 @@ -1966,7 +1969,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 @@ -1977,7 +1980,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
54 changes: 35 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,26 @@ 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;
FieldAttributes attr = field.Attributes & FieldAttributes.FieldAccessMask;
fieldValue["__section"] = attr == FieldAttributes.Public
? "public" :
attr == FieldAttributes.Private ? "private" : "internal";

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);

// Invoke getters, and check values
Expand Down
Loading

0 comments on commit b0766a9

Please sign in to comment.