Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -757,11 +757,11 @@ internal async Task<ValueOrError<JToken>> RuntimeGetPropertiesInternal(SessionId
}
case "valuetype":
{
var resValType = await context.SdbAgent.GetValueTypeValues(objectId.Value, accessorPropertiesOnly, token);
var resValType = await context.SdbAgent.GetValueTypeValues(objectId.Value, accessorPropertiesOnly, token, sortByAccessLevel);
return resValType switch
{
null => ValueOrError<JToken>.WithError($"Could not get properties for {objectId}"),
_ => ValueOrError<JToken>.WithValue(sortByAccessLevel ? JObject.FromObject(new { result = resValType }) : resValType)
_ => ValueOrError<JToken>.WithValue(resValType)
};
}
case "array":
Expand Down
154 changes: 105 additions & 49 deletions src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -710,23 +710,23 @@ public FieldTypeClass(int id, string name, int typeId, bool isNotPrivate, FieldA
internal class ValueTypeClass
{
public byte[] valueTypeBuffer;
public JArray valueTypeJson;
public JArray valueTypeJsonProps;
public JObject json;
public JArray jsonProps;
public int typeId;
public JArray valueTypeProxy;
public string valueTypeVarName;
public bool valueTypeAutoExpand;
public int Id;
public ValueTypeClass(string varName, byte[] buffer, JArray json, int id, bool expand_properties, int valueTypeId)
public string Id;
public ValueTypeClass(string varName, byte[] buffer, JObject json, int id, bool expand_properties, int valueTypeId)
{
valueTypeBuffer = buffer;
valueTypeJson = json;
this.json = json;
typeId = id;
valueTypeJsonProps = null;
jsonProps = null;
valueTypeProxy = null;
valueTypeVarName = varName;
valueTypeAutoExpand = expand_properties;
Id = valueTypeId;
Id = $"dotnet:valuetype:{valueTypeId}";
}
}
internal class PointerValue
Expand Down Expand Up @@ -1632,7 +1632,7 @@ public async Task<int> GetPropertyMethodIdByName(int typeId, string propertyName
return -1;
}

public async Task<JArray> CreateJArrayForProperties(int typeId, ElementType elementType, ArraySegment<byte> object_buffer, JArray attributes, bool isAutoExpandable, string objectIdStr, bool isOwn, CancellationToken token)
public async Task<JArray> CreateJArrayForProperties(int typeId, ElementType elementType, ArraySegment<byte> object_buffer, bool isAutoExpandable, string objectIdStr, bool isOwn, CancellationToken token, params JToken[] attributesCollections)
{
JArray ret = new JArray();
using var retDebuggerCmdReader = await GetTypePropertiesReader(typeId, token);
Expand All @@ -1651,32 +1651,36 @@ public async Task<JArray> CreateJArrayForProperties(int typeId, ElementType elem
if (getMethodId == 0 || await GetParamCount(getMethodId, token) != 0 || await MethodIsStatic(getMethodId, token))
continue;
JObject propRet = null;
if (attributes.Where(attribute => attribute["name"].Value<string>().Equals(propertyNameStr)).Any())
if (attributesCollections
.Any(attributes => attributes
.Where(attribute => attribute["name"].Value<string>().Equals(propertyNameStr)).Any()))
continue;
if (isAutoExpandable)
{
try {
try
{
propRet = await InvokeMethod(object_buffer, getMethodId, propertyNameStr, token);
}
catch (Exception)
catch (Exception ex)
{
continue;
logger.LogDebug($"Method {getMethodId} of object {objectId.Value} could not be ivoked as AutoExpandable, error - {ex.Message}.");
}
}
else
if (propRet == null)
{
propRet = JObject.FromObject(new {
get = new
{
type = "function",
objectId = $"dotnet:methodId:{objectId.Value}:{getMethodId}:{elementType}",
className = "Function",
description = "get " + propertyNameStr + " ()",
methodId = getMethodId,
objectIdValue = objectIdStr
},
name = propertyNameStr
});
propRet = JObject.FromObject(new
{
get = new
{
type = "function",
objectId = $"dotnet:methodId:{objectId.Value}:{getMethodId}:{elementType}",
className = "Function",
description = "get " + propertyNameStr + " ()",
methodId = getMethodId,
objectIdValue = objectIdStr
},
name = propertyNameStr
});
}
if (isOwn)
propRet["isOwn"] = true;
Expand All @@ -1698,25 +1702,34 @@ public async Task<JObject> GetPointerContent(int pointerId, CancellationToken to

public async Task<JArray> GetPropertiesValuesOfValueType(int valueTypeId, CancellationToken token)
{
JArray ret = new JArray();
JArray properties = new JArray();
var valueType = valueTypes[valueTypeId];
using var commandParamsWriter = new MonoBinaryWriter();
commandParamsWriter.Write(valueType.typeId);
using var retDebuggerCmdReader = await SendDebuggerAgentCommand(CmdType.GetParents, commandParamsWriter, token);
var parentsCount = retDebuggerCmdReader.ReadInt32();
List<int> typesToGetProperties = new List<int>();
typesToGetProperties.Add(valueType.typeId);
for (int i = 0 ; i < parentsCount; i++)
for (int i = 0; i < parentsCount; i++)
{
typesToGetProperties.Add(retDebuggerCmdReader.ReadInt32());
}
for (int i = 0 ; i < typesToGetProperties.Count; i++)
for (int i = 0; i < typesToGetProperties.Count; i++)
{
var properties = await CreateJArrayForProperties(typesToGetProperties[i], ElementType.ValueType, valueType.valueTypeBuffer, valueType.valueTypeJson, valueType.valueTypeAutoExpand, $"dotnet:valuetype:{valueType.Id}", i == 0, token);
ret = new JArray(ret.Union(properties));
var props = await CreateJArrayForProperties(
typesToGetProperties[i],
ElementType.ValueType,
valueType.valueTypeBuffer,
valueType.valueTypeAutoExpand,
valueType.Id,
isOwn: i == 0,
token,
valueType.json["result"],
valueType.json["internalProperties"],
valueType.json["privateProperties"]);
properties.AddRange(props);
}

return ret;
return properties;
}

private static bool AutoExpandable(string className) {
Expand Down Expand Up @@ -1870,7 +1883,6 @@ public async Task<JObject> CreateJObjectForValueType(MonoBinaryReader retDebugge
var description = className;
var numFields = retDebuggerCmdReader.ReadInt32();
var fields = await GetTypeFields(typeId, token);
JArray valueTypeFields = new JArray();
if (className.IndexOf("System.Nullable<") == 0) //should we call something on debugger-agent to check???
{
retDebuggerCmdReader.ReadByte(); //ignoring the boolean type
Expand All @@ -1881,10 +1893,28 @@ public async Task<JObject> CreateJObjectForValueType(MonoBinaryReader retDebugge
else
return CreateJObject<string>(null, "object", className, false, className, null, null, "null", true);
}
for (int i = 0; i < numFields ; i++)

JArray fieldsPublic = new JArray();
JArray fieldsInternal = new JArray();
JArray fieldsPrivate = new JArray();
for (int i = 0; i < numFields; i++)
{
fieldValueType = await CreateJObjectForVariableValue(retDebuggerCmdReader, fields.ElementAt(i).Name, true, fields.ElementAt(i).TypeId, false, token);
valueTypeFields.Add(fieldValueType);
fieldValueType = await CreateJObjectForVariableValue(retDebuggerCmdReader, fields[i].Name, true, fields[i].TypeId, false, token);
switch (fields[i].ProtectionLevel)
{
case FieldAttributes.Public:
fieldsPublic.Add(fieldValueType);
break;
case FieldAttributes.FamANDAssem:
case FieldAttributes.Private:
fieldsPrivate.Add(fieldValueType);
break;
case FieldAttributes.Family:
case FieldAttributes.Assembly:
case FieldAttributes.FamORAssem:
fieldsInternal.Add(fieldValueType);
break;
}
}

long endPos = retDebuggerCmdReader.BaseStream.Position;
Expand All @@ -1894,6 +1924,12 @@ public async Task<JObject> CreateJObjectForValueType(MonoBinaryReader retDebugge
byte[] valueTypeBuffer = new byte[endPos - initialPos];
retDebuggerCmdReader.Read(valueTypeBuffer, 0, (int)(endPos - initialPos));
retDebuggerCmdReader.BaseStream.Position = endPos;
var valueTypeFields = JObject.FromObject(new
{
result = fieldsPublic,
internalProperties = fieldsInternal,
privateProperties = fieldsPrivate
});
valueTypes[valueTypeId] = new ValueTypeClass(name, valueTypeBuffer, valueTypeFields, typeId, AutoExpandable(className), valueTypeId);
if (AutoInvokeToString(className) || isEnum == 1) {
int methodId = await GetMethodIdByName(typeId, "ToString", token);
Expand Down Expand Up @@ -2202,33 +2238,53 @@ public async Task<JArray> StackFrameGetValues(MethodInfoWithDebugInformation met

}

public async Task<JArray> GetValueTypeValues(int valueTypeId, bool accessorPropertiesOnly, CancellationToken token)
public async Task<JToken> GetValueTypeValues(int valueTypeId, bool accessorPropertiesOnly, CancellationToken token, bool sortByAccessLevel)
{
if (!valueTypes.TryGetValue(valueTypeId, out ValueTypeClass valueType))
return null;
if (valueType.valueTypeJsonProps == null)
{
valueType.valueTypeJsonProps = await GetPropertiesValuesOfValueType(valueTypeId, token);
}
if (valueType.jsonProps == null)
valueType.jsonProps = await GetPropertiesValuesOfValueType(valueTypeId, token);

if (accessorPropertiesOnly)
return valueType.valueTypeJsonProps;
var ret = new JArray(valueType.valueTypeJson.Union(valueType.valueTypeJsonProps));
return ret;
return sortByAccessLevel ?
JObject.FromObject(new
{
result = valueTypes[valueTypeId].jsonProps,
internalProperties = new JArray(),
privateProperties = new JArray()
}) :
valueTypes[valueTypeId].jsonProps;
var publicValues = valueTypes[valueTypeId].json["result"].Union(valueTypes[valueTypeId].jsonProps);
var internalValues = valueTypes[valueTypeId].json["internalProperties"];
var privateValues = valueTypes[valueTypeId].json["privateProperties"];

return sortByAccessLevel ?
JObject.FromObject(new
{
result = publicValues,
internalProperties = internalValues,
privateProperties = privateValues
}) :
new JArray(publicValues.Union(internalValues).Union(privateValues));
}

public async Task<JArray> GetValueTypeProxy(int valueTypeId, CancellationToken token)
{
if (valueTypes[valueTypeId].valueTypeProxy != null)
return valueTypes[valueTypeId].valueTypeProxy;
valueTypes[valueTypeId].valueTypeProxy = new JArray(valueTypes[valueTypeId].valueTypeJson);

var retDebuggerCmdReader = await GetTypePropertiesReader(valueTypes[valueTypeId].typeId, token);
valueTypes[valueTypeId].valueTypeProxy = new JArray(
valueTypes[valueTypeId].json["result"],
valueTypes[valueTypeId].json["internalProperties"],
valueTypes[valueTypeId].json["privateProperties"]);

var retDebuggerCmdReader = await GetTypePropertiesReader(valueTypes[valueTypeId].typeId, token);
if (retDebuggerCmdReader == null)
return null;

var nProperties = retDebuggerCmdReader.ReadInt32();

for (int i = 0 ; i < nProperties; i++)
for (int i = 0; i < nProperties; i++)
{
retDebuggerCmdReader.ReadInt32(); //propertyId
string propertyNameStr = retDebuggerCmdReader.ReadString();
Expand Down Expand Up @@ -2451,11 +2507,11 @@ public async Task<JArray> GetObjectValues(int objectId, GetObjectCommandOptions
typeId,
ElementType.Class,
commandParamsObjWriter.GetParameterBuffer(),
new JArray(objects.Values),
getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute),
$"dotnet:object:{objectId}",
i == 0,
token);
token,
new JArray(objects.Values));
var properties = await GetProperties(props, allFields, typeId, token);
objects.TryAddRange(properties);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -997,46 +997,6 @@ await RuntimeEvaluateAndCheck(
("a.valueToCheck", TNumber(20)));
});

[Fact]
public async Task EvaluateProtectionLevels() => await CheckInspectLocalsAtBreakpointSite(
"DebuggerTests.GetPropertiesTests.DerivedClass", "InstanceMethod", 1, "InstanceMethod",
"window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.GetPropertiesTests.DerivedClass:run'); })",
wait_for_event_fn: async (pause_location) =>
{
var id = pause_location["callFrames"][0]["callFrameId"].Value<string>();
var (obj, _) = await EvaluateOnCallFrame(id, "this");
var (pub, internalAndProtected, priv) = await GetPropertiesSortedByProtectionLevels(obj["objectId"]?.Value<string>());

await CheckProps(pub, new
{
a = TNumber(4),
Base_AutoStringPropertyForOverrideWithField = TString("DerivedClass#Base_AutoStringPropertyForOverrideWithField"),
Base_GetterForOverrideWithField = TString("DerivedClass#Base_GetterForOverrideWithField"),
BaseBase_MemberForOverride = TString("DerivedClass#BaseBase_MemberForOverride"),
DateTime = TGetter("DateTime", TDateTime(new DateTime(2200, 5, 6, 7, 18, 9))),
_DTProp = TGetter("_DTProp", TDateTime(new DateTime(2200, 5, 6, 7, 8, 9))),
FirstName = TGetter("FirstName", TString("DerivedClass#FirstName")),
_base_dateTime = TGetter("_base_dateTime", TDateTime(new DateTime(2134, 5, 7, 1, 9, 2))),
LastName = TGetter("LastName", TString("BaseClass#LastName"))
}, "public");

await CheckProps(internalAndProtected, new
{
base_num = TNumber(5)
}, "internalAndProtected");

await CheckProps(priv, new
{
_stringField = TString("DerivedClass#_stringField"),
_dateTime = TDateTime(new DateTime(2020, 7, 6, 5, 4, 3)),
AutoStringProperty = TString("DerivedClass#AutoStringProperty"),
StringPropertyForOverrideWithAutoProperty = TString("DerivedClass#StringPropertyForOverrideWithAutoProperty"),
_base_name = TString("private_name"),
Base_AutoStringProperty = TString("base#Base_AutoStringProperty"),
DateTimeForOverride = TGetter("DateTimeForOverride", TDateTime(new DateTime(2190, 9, 7, 5, 3, 2)))
}, "private");
});

[Fact]
public async Task StructureGetters() => await CheckInspectLocalsAtBreakpointSite(
"DebuggerTests.StructureGetters", "Evaluate", 2, "Evaluate",
Expand Down
Loading