Skip to content
Merged
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
2 changes: 2 additions & 0 deletions src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,8 @@ public ExecutionContext(MonoSDBHelper sdbAgent, int id, object auxData, PauseOnE

public object AuxData { get; set; }

public bool AutoEvaluateProperties { get; set; }

public PauseOnExceptionsKind PauseOnExceptions { get; set; }

public List<Frame> CallStack { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ async Task AddProperty(
{
try
{
propRet = await sdbHelper.InvokeMethod(getterParamsBuffer, getMethodId, token, name: propNameWithSufix);
propRet = await sdbHelper.InvokeMethod(getterParamsBuffer, getMethodId, token, name: propNameWithSufix, isPropertyStatic && !isValueType);
}
catch (Exception)
{
Expand Down
35 changes: 29 additions & 6 deletions src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,21 @@ protected override async Task<bool> AcceptCommand(MessageId id, JObject parms, C
return false;
}

case "Runtime.callFunctionOn":
{
try {
return await CallOnFunction(id, args, token);
}
catch (Exception ex) {
logger.LogDebug($"Runtime.callFunctionOn failed for {id} with args {args}: {ex}");
SendResponse(id,
Result.Exception(new ArgumentException(
$"Runtime.callFunctionOn not supported with ({args["objectId"]}).")),
token);
return true;
}
}

// Protocol extensions
case "DotnetDebugger.setDebuggerProperty":
{
Expand Down Expand Up @@ -552,19 +567,25 @@ protected override async Task<bool> AcceptCommand(MessageId id, JObject parms, C
SendResponse(id, await GetMethodLocation(id, args, token), token);
return true;
}
case "Runtime.callFunctionOn":
case "DotnetDebugger.setEvaluationOptions":
{
//receive the available options from DAP to variables, stack and evaluate commands.
try {
return await CallOnFunction(id, args, token);
if (args["options"]?["noFuncEval"]?.Value<bool>() == true)
context.AutoEvaluateProperties = false;
else
context.AutoEvaluateProperties = true;
SendResponse(id, Result.OkFromObject(new { }), token);
}
catch (Exception ex) {
logger.LogDebug($"Runtime.callFunctionOn failed for {id} with args {args}: {ex}");
catch (Exception ex)
{
logger.LogDebug($"DotnetDebugger.setEvaluationOptions failed for {id} with args {args}: {ex}");
SendResponse(id,
Result.Exception(new ArgumentException(
$"Runtime.callFunctionOn not supported with ({args["objectId"]}).")),
$"DotnetDebugger.setEvaluationOptions got incorrect argument ({args})")),
token);
return true;
}
return true;
}
}
// for Dotnetdebugger.* messages, treat them as handled, thus not passing them on to the browser
Expand Down Expand Up @@ -737,6 +758,8 @@ internal async Task<ValueOrError<GetMembersResult>> RuntimeGetObjectMembers(Sess
if (args["forDebuggerDisplayAttribute"]?.Value<bool>() == true)
getObjectOptions |= GetObjectCommandOptions.ForDebuggerDisplayAttribute;
}
if (context.AutoEvaluateProperties)
getObjectOptions |= GetObjectCommandOptions.AutoExpandable;
if (JustMyCode)
getObjectOptions |= GetObjectCommandOptions.JustMyCode;
try
Expand Down
5 changes: 3 additions & 2 deletions src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1821,11 +1821,12 @@ public async Task<string> GetDelegateMethodDescription(int objectId, Cancellatio
return $"{returnType} {methodName} {parameters}";
}

public async Task<JObject> InvokeMethod(ArraySegment<byte> argsBuffer, int methodId, CancellationToken token, string name = null)
public async Task<JObject> InvokeMethod(ArraySegment<byte> argsBuffer, int methodId, CancellationToken token, string name = null, bool isMethodStatic = false)
{
using var commandParamsWriter = new MonoBinaryWriter();
commandParamsWriter.Write(methodId);
commandParamsWriter.Write(argsBuffer);
if (!isMethodStatic)
commandParamsWriter.Write(argsBuffer);
commandParamsWriter.Write(0);
using var retDebuggerCmdReader = await SendDebuggerAgentCommand(CmdVM.InvokeMethod, commandParamsWriter, token);
retDebuggerCmdReader.ReadByte(); //number of objects returned.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace BrowserDebugProxy
{
internal sealed class ValueTypeClass
{
private readonly bool autoExpand;
private bool autoExpand;
private JArray proxy;
private GetMembersResult _combinedResult;
private bool propertiesExpanded;
Expand Down Expand Up @@ -201,6 +201,8 @@ public async Task<JArray> GetProxy(MonoSDBHelper sdbHelper, CancellationToken to
public async Task<GetMembersResult> GetMemberValues(
MonoSDBHelper sdbHelper, GetObjectCommandOptions getObjectOptions, bool sortByAccessLevel, bool includeStatic, CancellationToken token)
{
if (getObjectOptions.HasFlag(GetObjectCommandOptions.AutoExpandable) && !getObjectOptions.HasFlag(GetObjectCommandOptions.AccessorPropertiesOnly))
autoExpand = true;
// 1
if (!propertiesExpanded)
{
Expand Down
78 changes: 58 additions & 20 deletions src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ namespace DebuggerTests
{
public class GetPropertiesTests : DebuggerTests
{
public enum AutoEvaluate
{
On,
Off,
Unset,
}

public GetPropertiesTests(ITestOutputHelper testOutput) : base(testOutput)
{}

Expand Down Expand Up @@ -253,24 +260,8 @@ public GetPropertiesTests(ITestOutputHelper testOutput) : base(testOutput)
[MemberData(nameof(ClassGetPropertiesTestData), parameters: false)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: true)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: false)]
public async Task InspectTypeInheritedMembers(string type_name, bool? own_properties, bool? accessors_only, string[] expected_names, Dictionary<string, (JObject, bool)> all_props, bool is_async) => await CheckInspectLocalsAtBreakpointSite(
$"DebuggerTests.GetPropertiesTests.{type_name}",
$"InstanceMethod{(is_async ? "Async" : "")}", 1, $"DebuggerTests.GetPropertiesTests.{type_name}." + (is_async ? "InstanceMethodAsync" : "InstanceMethod"),
$"window.setTimeout(function() {{ invoke_static_method_async ('[debugger-test] DebuggerTests.GetPropertiesTests.{type_name}:run'); }})",
wait_for_event_fn: async (pause_location) =>
{
var frame_id = pause_location["callFrames"][0]["callFrameId"].Value<string>();
var frame_locals = await GetProperties(frame_id);
var this_obj = GetAndAssertObjectWithName(frame_locals, "this");
var this_props = await GetProperties(this_obj["value"]?["objectId"]?.Value<string>(), own_properties: own_properties, accessors_only: accessors_only);

AssertHasOnlyExpectedProperties(expected_names, this_props.Values<JObject>());
await CheckExpectedProperties(expected_names, name => GetAndAssertObjectWithName(this_props, name), all_props);

// indexer properties shouldn't show up here
var item = this_props.FirstOrDefault(jt => jt["name"]?.Value<string>() == "Item");
Assert.Null(item);
});
public async Task InspectTypeInheritedMembers(string type_name, bool? own_properties, bool? accessors_only, string[] expected_names, Dictionary<string, (JObject, bool)> all_props, bool is_async) =>
await InspectTypeInheritedMembersInternal(type_name, own_properties, accessors_only, expected_names, all_props, is_async, AutoEvaluate.Unset);

public static IEnumerable<object[]> MembersForLocalNestedStructData(bool is_async)
=> StructGetPropertiesTestData(false).Select(datum => datum[1..]);
Expand Down Expand Up @@ -444,7 +435,7 @@ await EvaluateOnCallFrameAndCheck(frame_id,
);
}

private async Task CheckExpectedProperties(string[] expected_names, Func<string, JToken> get_actual_prop, Dictionary<string, (JObject, bool)> all_props)
private async Task CheckExpectedProperties(string[] expected_names, Func<string, JToken> get_actual_prop, Dictionary<string, (JObject, bool)> all_props, bool autoEvaluate = false)
{
foreach (var exp_name in expected_names)
{
Expand All @@ -463,7 +454,13 @@ private async Task CheckExpectedProperties(string[] expected_names, Func<string,
// from `{name: "..", value: {}, ..}
// but for getters we actually have: `{name: "..", get: {..} }`
// and no `value`
await CheckValue(actual_prop, exp_prop, exp_name);
if (!autoEvaluate)
await CheckValue(actual_prop, exp_prop, exp_name);
else
{
if (exp_prop["value"].Type != JTokenType.Null)
await CheckValue(actual_prop["value"], exp_prop["value"], exp_name);
}
}
else
{
Expand Down Expand Up @@ -623,5 +620,46 @@ await CheckInspectLocalsAtBreakpointSite(
await CheckProps(pubInternalAndProtected, expectedPublicInternalAndProtected, "result");
await CheckProps(priv, expectedPriv, "private");
});

[ConditionalTheory(nameof(RunningOnChrome))]
[MemberData(nameof(ClassGetPropertiesTestData), parameters: true)]
[MemberData(nameof(ClassGetPropertiesTestData), parameters: false)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: true)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: false)]
public async Task InspectTypeInheritedMembersAutoEvaluateOn(string type_name, bool? own_properties, bool? accessors_only, string[] expected_names, Dictionary<string, (JObject, bool)> all_props, bool is_async) =>
await InspectTypeInheritedMembersInternal(type_name, own_properties, accessors_only, expected_names, all_props, is_async, AutoEvaluate.On);

[ConditionalTheory(nameof(RunningOnChrome))]
[MemberData(nameof(ClassGetPropertiesTestData), parameters: true)]
[MemberData(nameof(ClassGetPropertiesTestData), parameters: false)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: true)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: false)]
public async Task InspectTypeInheritedMembersAutoEvaluateOff(string type_name, bool? own_properties, bool? accessors_only, string[] expected_names, Dictionary<string, (JObject, bool)> all_props, bool is_async) =>
await InspectTypeInheritedMembersInternal(type_name, own_properties, accessors_only, expected_names, all_props, is_async, AutoEvaluate.Off);

public async Task InspectTypeInheritedMembersInternal(string type_name, bool? own_properties, bool? accessors_only, string[] expected_names, Dictionary<string, (JObject, bool)> all_props, bool is_async, AutoEvaluate auto_evaluate_status)
{
if (auto_evaluate_status == AutoEvaluate.On)
await cli.SendCommand("DotnetDebugger.setEvaluationOptions", JObject.FromObject(new { options = new { noFuncEval = false } }), token);
else if (auto_evaluate_status == AutoEvaluate.Off)
await cli.SendCommand("DotnetDebugger.setEvaluationOptions", JObject.FromObject(new { options = new { noFuncEval = true } }), token);
await CheckInspectLocalsAtBreakpointSite(
$"DebuggerTests.GetPropertiesTests.{type_name}",
$"InstanceMethod{(is_async ? "Async" : "")}", 1, $"DebuggerTests.GetPropertiesTests.{type_name}." + (is_async ? "InstanceMethodAsync" : "InstanceMethod"),
$"window.setTimeout(function() {{ invoke_static_method_async ('[debugger-test] DebuggerTests.GetPropertiesTests.{type_name}:run'); }})",
wait_for_event_fn: async (pause_location) =>
{
var frame_id = pause_location["callFrames"][0]["callFrameId"].Value<string>();
var frame_locals = await GetProperties(frame_id);
var this_obj = GetAndAssertObjectWithName(frame_locals, "this");
var this_props = await GetProperties(this_obj["value"]?["objectId"]?.Value<string>(), own_properties: own_properties, accessors_only: accessors_only);
AssertHasOnlyExpectedProperties(expected_names, this_props.Values<JObject>());
await CheckExpectedProperties(expected_names, name => GetAndAssertObjectWithName(this_props, name), all_props, auto_evaluate_status == AutoEvaluate.On);

// indexer properties shouldn't show up here
var item = this_props.FirstOrDefault(jt => jt["name"]?.Value<string>() == "Item");
Assert.Null(item);
});
}
}
}