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
51 changes: 50 additions & 1 deletion src/mono/mono/mini/mini-wasm-debugger.c
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,22 @@ read_enum_value (const char *mem, int type)
return 0;
}

static gboolean
nullable_try_get_value (guint8 *nullable, MonoClass *klass, gpointer* out_value)
{
mono_class_setup_fields (klass);
g_assert (m_class_is_fields_inited (klass));

*out_value = NULL;
MonoClassField *klass_fields = m_class_get_fields (klass);
gpointer addr_for_has_value = mono_vtype_get_field_addr (nullable, &klass_fields[0]);
if (0 == *(guint8*)addr_for_has_value)
return FALSE;

*out_value = mono_vtype_get_field_addr (nullable, &klass_fields[1]);
return TRUE;
}

static gboolean
describe_value(MonoType * type, gpointer addr, int gpflags)
{
Expand Down Expand Up @@ -899,7 +915,41 @@ 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)) {
MonoType *targ = type->data.generic_class->context.class_inst->type_argv [0];

gpointer nullable_value = NULL;
if (nullable_try_get_value (addr, klass, &nullable_value)) {
return describe_value (targ, nullable_value, gpflags);
} else {
char* class_name = mono_type_full_name (type);
mono_wasm_add_obj_var (class_name, NULL, 0);
g_free (class_name);
break;
}
}

if (mono_type_generic_inst_is_valuetype (type))
goto handle_vtype;
/*
Expand All @@ -909,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
104 changes: 94 additions & 10 deletions src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,43 @@ await CheckProps(strings_arr, new[]
}
);

[Theory]
[InlineData("TestNullableLocal", false)]
[InlineData("TestNullableLocalAsync", true)]
public async Task InspectNullableLocals(string method_name, bool is_async) => await CheckInspectLocalsAtBreakpointSite(
"DebuggerTests.NullableTests",
method_name,
10,
is_async ? "MoveNext" : method_name,
$"window.setTimeout(function() {{ invoke_static_method_async('[debugger-test] DebuggerTests.NullableTests:{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_int = TNumber(5),
n_int_null = TObject("System.Nullable<int>", null),

n_dt = TDateTime(dt),
n_dt_null = TObject("System.Nullable<System.DateTime>", null),

n_gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct<int>"),
n_gs_null = TObject("System.Nullable<DebuggerTests.ValueTypesTest.GenericStruct<int>>", null),
}, "locals");

// check gs

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

[Theory]
[InlineData(false)]
[InlineData(true)]
Expand All @@ -419,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 @@ -594,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 @@ -612,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 @@ -631,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 @@ -979,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 @@ -1624,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
24 changes: 12 additions & 12 deletions src/mono/wasm/debugger/tests/debugger-get-properties-test.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public interface ILastName
}

public interface IName : IFirstName, ILastName
{}
{ }

public class BaseBaseClass
{
Expand Down Expand Up @@ -91,20 +91,20 @@ public DerivedClass()

public static void run()
{
new DerivedClass().InstanceMethod ();
new DerivedClass().InstanceMethodAsync ().Wait();
new DerivedClass().InstanceMethod();
new DerivedClass().InstanceMethodAsync().Wait();
}

public string GetStringField() => _stringField;

public void InstanceMethod()
{
Console.WriteLine ($"break here");
Console.WriteLine($"break here");
}

public async Task InstanceMethodAsync()
{
Console.WriteLine ($"break here");
Console.WriteLine($"break here");
await Task.CompletedTask;
}
}
Expand Down Expand Up @@ -138,20 +138,20 @@ public CloneableStruct(int bias)

public static void run()
{
new CloneableStruct(3).InstanceMethod ();
new CloneableStruct(3).InstanceMethodAsync ().Wait();
new CloneableStruct(3).InstanceMethod();
new CloneableStruct(3).InstanceMethodAsync().Wait();
}

public string GetStringField() => _stringField;

public void InstanceMethod()
{
Console.WriteLine ($"break here");
Console.WriteLine($"break here");
}

public async Task InstanceMethodAsync()
{
Console.WriteLine ($"break here");
Console.WriteLine($"break here");
await Task.CompletedTask;
}
}
Expand All @@ -174,13 +174,13 @@ public static void run()
public static void TestNestedStructStatic()
{
var ns = new NestedStruct(3);
Console.WriteLine ($"break here");
Console.WriteLine($"break here");
}

public static async Task TestNestedStructStaticAsync()
{
var ns = new NestedStruct(3);
Console.WriteLine ($"break here");
Console.WriteLine($"break here");
await Task.CompletedTask;
}
}
Expand All @@ -200,7 +200,7 @@ class DerivedClassForJSTest : BaseClassForJSTest
public static void run()
{
var obj = new DerivedClassForJSTest();
Console.WriteLine ($"break here");
Console.WriteLine($"break here");
}
}
}
40 changes: 40 additions & 0 deletions src/mono/wasm/debugger/tests/debugger-nullable-test.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

#nullable enable

namespace DebuggerTests
{
public class NullableTests
{
public static void TestNullableLocal()
{
int? n_int = 5;
int? n_int_null = null;

DateTime? n_dt = new DateTime(2310, 1, 2, 3, 4, 5);
DateTime? n_dt_null = null;

ValueTypesTest.GenericStruct<int>? n_gs = new ValueTypesTest.GenericStruct<int> { StringField = "n_gs#StringField" };
ValueTypesTest.GenericStruct<int>? n_gs_null = null;

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

public static async Task TestNullableLocalAsync()
{
int? n_int = 5;
int? n_int_null = null;

DateTime? n_dt = new DateTime(2310, 1, 2, 3, 4, 5);
DateTime? n_dt_null = null;

ValueTypesTest.GenericStruct<int>? n_gs = new ValueTypesTest.GenericStruct<int> { StringField = "n_gs#StringField" };
ValueTypesTest.GenericStruct<int>? n_gs_null = null;

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