Skip to content

Commit

Permalink
[wasm][debugger] Show actual data for boxed values (#41562)
Browse files Browse the repository at this point in the history
* [wasm][debugger] Add support for Nullable<T>

Return the value, or null.

Fixes mono/mono#20310

* Address review feedback - merge functions

* [wasm][debugger] run dotnet-format on the debugger test app

* [wasm][debugger] simplify function sig, based on usage

- addresses review feedback from @lewing

* [wasm][debugger] Simplify the function further, based on @lewing's

.. excellent suggestion!

* [wasm][debugger] Show actual data for boxed values

Eg. `object o = "foobar"`

This will show the string `"foobar"`, instead of an object, in the
debugger.
  • Loading branch information
radical authored Sep 5, 2020
1 parent 66f4b4b commit 4fd87bc
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 13 deletions.
20 changes: 19 additions & 1 deletion src/mono/mono/mini/mini-wasm-debugger.c
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,25 @@ describe_value(MonoType * type, gpointer addr, int gpflags)
}
break;
}

case MONO_TYPE_OBJECT: {
MonoObject *obj = *(MonoObject**)addr;
MonoClass *klass = obj->vtable->klass;
if (!klass) {
// boxed null
mono_wasm_add_obj_var ("object", NULL, 0);
break;
}

type = m_class_get_byval_arg (klass);

// Boxed valuetype
if (m_class_is_valuetype (klass))
addr = mono_object_unbox_internal (obj);

return describe_value (type, addr, gpflags);
}

case MONO_TYPE_GENERICINST: {
MonoClass *klass = mono_class_from_mono_type_internal (type);
if (mono_class_is_nullable (klass)) {
Expand All @@ -940,7 +959,6 @@ describe_value(MonoType * type, gpointer addr, int gpflags)

case MONO_TYPE_SZARRAY:
case MONO_TYPE_ARRAY:
case MONO_TYPE_OBJECT:
case MONO_TYPE_CLASS: {
MonoObject *obj = *(MonoObject**)addr;
MonoClass *klass = type->data.klass;
Expand Down
67 changes: 57 additions & 10 deletions src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -456,12 +456,6 @@ await CheckInspectLocalsAtBreakpointSite(
}
);

object TGenericStruct(string typearg, string stringField) => new
{
List = TObject($"System.Collections.Generic.List<{typearg}>"),
StringField = TString(stringField)
};

[Fact]
public async Task RuntimeGetPropertiesWithInvalidScopeIdTest()
{
Expand Down Expand Up @@ -631,7 +625,7 @@ await insp.Ready(async (cli, token) =>
Assert.Equal(3, props.Count());
CheckNumber(props, "A", 10);
CheckString(props, "B", "xx");
CheckObject(props, "c", "object");
CheckString(props, "c", "20_xx");

// Check UseComplex frame
var locals_m1 = await GetLocalsForFrame(pause_location["callFrames"][3], debugger_test_loc, 23, 8, "UseComplex");
Expand All @@ -649,7 +643,7 @@ await insp.Ready(async (cli, token) =>
Assert.Equal(3, props.Count());
CheckNumber(props, "A", 10);
CheckString(props, "B", "xx");
CheckObject(props, "c", "object");
CheckString(props, "c", "20_xx");

pause_location = await StepAndCheck(StepKind.Over, dep_cs_loc, 23, 8, "DoStuff", times: 2);
// Check UseComplex frame again
Expand All @@ -668,7 +662,7 @@ await insp.Ready(async (cli, token) =>
Assert.Equal(3, props.Count());
CheckNumber(props, "A", 10);
CheckString(props, "B", "xx");
CheckObject(props, "c", "object");
CheckString(props, "c", "20_xx");
});
}

Expand Down Expand Up @@ -1016,6 +1010,59 @@ await CompareObjectPropertiesFor(vt_local_props, name,
});
}

[Theory]
[InlineData("BoxingTest", false)]
[InlineData("BoxingTestAsync", true)]
public async Task InspectBoxedLocals(string method_name, bool is_async) => await CheckInspectLocalsAtBreakpointSite(
"DebuggerTest",
method_name,
17,
is_async ? "MoveNext" : method_name,
$"window.setTimeout(function() {{ invoke_static_method_async('[debugger-test] DebuggerTest:{method_name}'); }}, 1);",
wait_for_event_fn: async (pause_location) =>
{
var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value<string>());
var dt = new DateTime(2310, 1, 2, 3, 4, 5);
await CheckProps(locals, new
{
n_i = TNumber(5),
o_i = TNumber(5),
o_n_i = TNumber(5),
o_s = TString("foobar"),
o_obj = TObject("Math"),

n_gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct<int>"),
o_gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct<int>"),
o_n_gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct<int>"),

n_dt = TDateTime(dt),
o_dt = TDateTime(dt),
o_n_dt = TDateTime(dt),

o_null = TObject("object", is_null: true),
o_ia = TArray("int[]", 2),
}, "locals");

foreach (var name in new[] { "n_gs", "o_gs", "o_n_gs" })
{
var gs = GetAndAssertObjectWithName(locals, name);
var gs_props = await GetProperties(gs["value"]?["objectId"]?.Value<string> ());
await CheckProps(gs_props, new
{
List = TObject("System.Collections.Generic.List<int>", is_null: true),
StringField = TString("n_gs#StringField"),
Options = TEnum ("DebuggerTests.Options", "None")
}, name);
}

var o_ia_props = await GetObjectOnLocals(locals, "o_ia");
await CheckProps(o_ia_props, new[]
{
TNumber(918),
TNumber(58971)
}, nameof(o_ia_props));
});

[Theory]
[InlineData(false)]
[InlineData(true)]
Expand Down Expand Up @@ -1661,7 +1708,7 @@ await insp.Ready(async (cli, token) =>
await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", 12, 8, "IntAdd");
bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8);
await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, "IntAdd");

});
}
//TODO add tests covering basic stepping behavior as step in/out/over
Expand Down
47 changes: 45 additions & 2 deletions src/mono/wasm/debugger/tests/debugger-test.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;

using System.Threading.Tasks;
public partial class Math
{ //Only append content to this class as the test suite depends on line info
public static int IntAdd(int a, int b)
Expand Down Expand Up @@ -285,7 +285,7 @@ async System.Threading.Tasks.Task MethodWithDelegatesAsync()
public delegate void DelegateWithVoidReturn(GenericStruct<int[]> gs);
public static void DelegateTargetWithVoidReturn(GenericStruct<int[]> gs) { }

delegate GenericStruct<bool[]> DelegateForSignatureTest(Math m, GenericStruct<GenericStruct<int[]>> gs);
public delegate GenericStruct<bool[]> DelegateForSignatureTest(Math m, GenericStruct<GenericStruct<int[]>> gs);
static bool DelegateTargetForNestedFunc<T>(T arg) => true;

public struct SimpleStruct
Expand Down Expand Up @@ -339,4 +339,47 @@ public static int locals()
}

static void locals_inner() { }

public static void BoxingTest()
{
int? n_i = 5;
object o_i = n_i.Value;
object o_n_i = n_i;

object o_s = "foobar";
object o_obj = new Math();
DebuggerTests.ValueTypesTest.GenericStruct<int>? n_gs = new DebuggerTests.ValueTypesTest.GenericStruct<int> { StringField = "n_gs#StringField" };
object o_gs = n_gs.Value;
object o_n_gs = n_gs;

DateTime? n_dt = new DateTime(2310, 1, 2, 3, 4, 5);
object o_dt = n_dt.Value;
object o_n_dt = n_dt;
object o_null = null;
object o_ia = new int[] {918, 58971};

Console.WriteLine ($"break here");
}

public static async Task BoxingTestAsync()
{
int? n_i = 5;
object o_i = n_i.Value;
object o_n_i = n_i;

object o_s = "foobar";
object o_obj = new Math();
DebuggerTests.ValueTypesTest.GenericStruct<int>? n_gs = new DebuggerTests.ValueTypesTest.GenericStruct<int> { StringField = "n_gs#StringField" };
object o_gs = n_gs.Value;
object o_n_gs = n_gs;

DateTime? n_dt = new DateTime(2310, 1, 2, 3, 4, 5);
object o_dt = n_dt.Value;
object o_n_dt = n_dt;
object o_null = null;
object o_ia = new int[] {918, 58971};

Console.WriteLine ($"break here");
await Task.CompletedTask;
}
}

0 comments on commit 4fd87bc

Please sign in to comment.