Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnkit Jain <radical@gmail.com>2020-04-09 09:51:15 +0300
committerAnkit Jain <radical@gmail.com>2020-04-10 19:44:30 +0300
commit5bbca1f930b350b03a2ecdf0c45586e825b5ef20 (patch)
treed9b27c62c656135181c44adae90ad7fc94cf046e
parentab15ba3d9979e8c3c5d42eb329940e0d28f15d0d (diff)
[wasm][debugger] For some types, use `ToString()` for their ..
.. `description`. - `System.DateTime` - `System.DateTimeOffset` - `System.Decimal` - `System.TimeSpan` - `System.Guid`
-rw-r--r--mono/mini/mini-wasm-debugger.c86
-rw-r--r--sdks/wasm/DebuggerTestSuite/Tests.cs103
-rw-r--r--sdks/wasm/debugger-valuetypes-test.cs103
-rw-r--r--sdks/wasm/src/library_mono.js12
4 files changed, 278 insertions, 26 deletions
diff --git a/mono/mini/mini-wasm-debugger.c b/mono/mini/mini-wasm-debugger.c
index 737028ff611..8586a2e2819 100644
--- a/mono/mini/mini-wasm-debugger.c
+++ b/mono/mini/mini-wasm-debugger.c
@@ -44,9 +44,9 @@ extern void mono_wasm_fire_bp (void);
extern void mono_wasm_add_bool_var (gint8);
extern void mono_wasm_add_number_var (double);
extern void mono_wasm_add_string_var (const char*);
-extern void mono_wasm_add_obj_var (const char*, guint64);
-extern void mono_wasm_add_value_type_unexpanded_var (const char*);
-extern void mono_wasm_begin_value_type_var (const char*);
+extern void mono_wasm_add_obj_var (const char*, const char*, guint64);
+extern void mono_wasm_add_value_type_unexpanded_var (const char*, const char*);
+extern void mono_wasm_begin_value_type_var (const char*, const char*);
extern void mono_wasm_end_value_type_var (void);
extern void mono_wasm_add_enum_var (const char*, const char*, guint64);
extern void mono_wasm_add_func_var (const char*, guint64);
@@ -67,6 +67,15 @@ static GHashTable *objrefs;
static GHashTable *obj_to_objref;
static int objref_id = 0;
+static const char*
+to_string_as_descr_names[] = {
+ "System.DateTime",
+ "System.DateTimeOffset",
+ "System.Decimal",
+ "System.TimeSpan",
+ "System.Guid"
+};
+
#define THREAD_TO_INTERNAL(thread) (thread)->internal_thread
static void
@@ -616,6 +625,62 @@ mono_wasm_enum_frames (void)
mono_walk_stack_with_ctx (list_frames, NULL, MONO_UNWIND_NONE, NULL);
}
+static char*
+invoke_to_string (MonoClass *klass, gpointer addr)
+{
+ MonoObject *exc;
+ MonoString *mstr;
+ char *ret_str;
+ ERROR_DECL (error);
+ MonoObject *obj;
+
+ // TODO: this is for a specific use case right now,
+ // (invoke ToString() get a preview/description for *some* types)
+ // and we don't want to report errors for that.
+ if (m_class_is_valuetype (klass)) {
+ MonoObject *boxed_obj = mono_value_box_checked (mono_domain_get (), klass, addr, error);
+ if (!is_ok (error))
+ return NULL;
+
+ obj = boxed_obj;
+ } else {
+ obj = *(MonoObject**)addr;
+ }
+
+ if (!obj)
+ return NULL;
+
+ mstr = mono_object_try_to_string (obj, &exc, error);
+ if (exc)
+ return NULL;
+
+ if (!is_ok (error))
+ return NULL;
+
+ ret_str = mono_string_to_utf8_checked_internal (mstr, error);
+ if (!is_ok (error))
+ return NULL;
+
+ // FREE boxed_obj
+
+ return ret_str;
+}
+
+static char*
+get_to_string_description (const char* class_name, MonoClass *klass, gpointer addr)
+{
+ if (!class_name || !klass || !addr)
+ return NULL;
+
+ for (int i = 0; i < G_N_ELEMENTS (to_string_as_descr_names); i ++) {
+ if (strcmp (to_string_as_descr_names [i], class_name) == 0) {
+ return invoke_to_string (klass, addr);
+ }
+ }
+
+ return NULL;
+}
+
typedef struct {
int cur_frame;
int target_frame;
@@ -734,7 +799,9 @@ static gboolean describe_value(MonoType * type, gpointer addr, gboolean expandVa
} else if (m_class_is_delegate (klass)) {
mono_wasm_add_func_var (class_name, obj_id);
} else {
- mono_wasm_add_obj_var (class_name, obj_id);
+ char *to_string_val = get_to_string_description (class_name, klass, addr);
+ mono_wasm_add_obj_var (class_name, to_string_val, obj_id);
+ g_free (to_string_val);
}
g_free (class_name);
break;
@@ -779,12 +846,17 @@ static gboolean describe_value(MonoType * type, gpointer addr, gboolean expandVa
mono_wasm_add_enum_var (class_name, enum_members->str, value__);
g_string_free (enum_members, TRUE);
} else if (expandValueType) {
- mono_wasm_begin_value_type_var (class_name);
+ char *to_string_val = get_to_string_description (class_name, klass, addr);
+ mono_wasm_begin_value_type_var (class_name, to_string_val);
+ g_free (to_string_val);
+
// FIXME: isAsyncLocalThis
describe_object_properties_for_klass ((MonoObject*)addr, klass, FALSE, expandValueType);
mono_wasm_end_value_type_var ();
} else {
- mono_wasm_add_value_type_unexpanded_var (class_name);
+ char *to_string_val = get_to_string_description (class_name, klass, addr);
+ mono_wasm_add_value_type_unexpanded_var (class_name, to_string_val);
+ g_free (to_string_val);
}
g_free (class_name);
break;
@@ -975,7 +1047,7 @@ describe_non_async_this (InterpFrame *frame, MonoMethod *method)
char *class_name = mono_class_full_name (obj->vtable->klass);
mono_wasm_add_properties_var ("this", -1);
- mono_wasm_add_obj_var (class_name, get_object_id(obj));
+ mono_wasm_add_obj_var (class_name, NULL, get_object_id(obj));
g_free (class_name);
}
}
diff --git a/sdks/wasm/DebuggerTestSuite/Tests.cs b/sdks/wasm/DebuggerTestSuite/Tests.cs
index 7da8614eb2f..ea0b6fc782e 100644
--- a/sdks/wasm/DebuggerTestSuite/Tests.cs
+++ b/sdks/wasm/DebuggerTestSuite/Tests.cs
@@ -251,7 +251,12 @@ namespace DebuggerTests
}
async Task CheckDateTime (JToken locals, string name, DateTime expected)
- => await CheckObjectOnLocals (locals, name,
+ {
+ var obj = locals.Where (jt => jt ["name"]?.Value<string> () == name)
+ .FirstOrDefault ();
+ Assert.Equal (expected.ToString (), obj ["value"]? ["description"]?.Value<string> ());
+
+ await CheckObjectOnLocals (locals, name,
test_fn: (members) => {
// not checking everything
#if false
@@ -273,6 +278,7 @@ namespace DebuggerTests
// FIXME: check some float properties too
}
);
+ }
JToken CheckBool (JToken locals, string name, bool expected)
{
@@ -907,7 +913,7 @@ namespace DebuggerTests
var ss_local_props = await CompareObjectPropertiesOnFrameLocals (pause_location ["callFrames"][0], "ss_local",
new {
str_member = TString ("set in MethodWithLocalStructs#SimpleStruct#str_member"),
- dt = TValueType ("System.DateTime"),
+ dt = TValueType ("System.DateTime", "2/3/2021 4:06:07 AM"),
gs = TValueType ("DebuggerTests.ValueTypesTest.GenericStruct<System.DateTime>"),
Kind = TEnum ("System.DateTimeKind", "Utc")
});
@@ -950,7 +956,7 @@ namespace DebuggerTests
await CompareObjectPropertiesFor (vt_local_props, "SimpleStructProperty",
new {
str_member = TString ("SimpleStructProperty#string#0#SimpleStruct#str_member"),
- dt = TValueType ("System.DateTime"),
+ dt = TValueType ("System.DateTime", "3/4/2022 5:07:08 AM"),
gs = TValueType ("DebuggerTests.ValueTypesTest.GenericStruct<System.DateTime>"),
Kind = TEnum ("System.DateTimeKind", "Utc")
},
@@ -959,7 +965,7 @@ namespace DebuggerTests
await CompareObjectPropertiesFor (vt_local_props, "SimpleStructField",
new {
str_member = TString ("SimpleStructField#string#0#SimpleStruct#str_member"),
- dt = TValueType ("System.DateTime"),
+ dt = TValueType ("System.DateTime", "6/7/2025 8:10:11 AM"),
gs = TValueType ("DebuggerTests.ValueTypesTest.GenericStruct<System.DateTime>"),
Kind = TEnum ("System.DateTimeKind", "Local")
},
@@ -998,7 +1004,7 @@ namespace DebuggerTests
var ss_local_as_ss_arg = new {
str_member = TString ("ss_local#SimpleStruct#string#0#SimpleStruct#str_member"),
- dt = TValueType ("System.DateTime"),
+ dt = TValueType ("System.DateTime", "6/7/2025 8:10:11 AM"),
gs = TValueType ("DebuggerTests.ValueTypesTest.GenericStruct<System.DateTime>"),
Kind = TEnum ("System.DateTimeKind", "Local")
};
@@ -1033,7 +1039,7 @@ namespace DebuggerTests
var ss_arg_updated = new {
str_member = TString ("ValueTypesTest#MethodWithStructArgs#updated#ss_arg#str_member"),
- dt = TValueType ("System.DateTime"),
+ dt = TValueType ("System.DateTime", "6/7/2025 8:10:11 AM"),
gs = TValueType ("DebuggerTests.ValueTypesTest.GenericStruct<System.DateTime>"),
Kind = TEnum ("System.DateTimeKind", "Utc")
};
@@ -1119,7 +1125,7 @@ namespace DebuggerTests
var ss_local_props = await CompareObjectPropertiesOnFrameLocals (pause_location ["callFrames"][0], "ss_local",
new {
str_member = TString ("set in MethodWithLocalStructsStaticAsync#SimpleStruct#str_member"),
- dt = TValueType ("System.DateTime"),
+ dt = TValueType ("System.DateTime", "2/3/2021 4:06:07 AM"),
gs = TValueType ("DebuggerTests.ValueTypesTest.GenericStruct<System.DateTime>"),
Kind = TEnum ("System.DateTimeKind", "Utc")
});
@@ -1650,6 +1656,74 @@ namespace DebuggerTests
});
}
+ [Theory]
+ [InlineData (123, 3, "MethodWithLocalsForToStringTest", false, false)]
+ [InlineData (133, 3, "MethodWithArgumentsForToStringTest", true, false)]
+ [InlineData (175, 3, "MethodWithArgumentsForToStringTestAsync", true, true)]
+ [InlineData (165, 3, "MethodWithArgumentsForToStringTestAsync", false, true)]
+ public async Task InspectLocalsForToStringDescriptions (int line, int col, string method_name, bool call_other, bool invoke_async)
+ {
+ var insp = new Inspector ();
+ //Collect events
+ var scripts = SubscribeToScripts(insp);
+ string entry_method_name = $"[debugger-test] DebuggerTests.ValueTypesTest:MethodWithLocalsForToStringTest{(invoke_async ? "Async" : String.Empty)}";
+ int frame_idx = 0;
+
+ await Ready();
+ await insp.Ready (async (cli, token) => {
+ ctx = new DebugTestContext (cli, insp, token, scripts);
+ var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs";
+
+ await SetBreakpoint (debugger_test_loc, line, col);
+
+ var eval_expr = "window.setTimeout(function() {"
+ + (invoke_async ? "invoke_static_method_async (" : "invoke_static_method (")
+ + $"'{entry_method_name}',"
+ + (call_other ? "true" : "false")
+ + "); }, 1);";
+ Console.WriteLine ($"{eval_expr}");
+
+ var pause_location = await EvaluateAndCheck (eval_expr, debugger_test_loc, line, col, invoke_async ? "MoveNext" : method_name);
+
+ var frame_locals = await CheckLocalsOnFrame (pause_location ["callFrames"][frame_idx],
+ new {
+ call_other = TBool (call_other),
+ dt0 = TValueType ("System.DateTime", "1/2/2020 3:04:05 AM"),
+ dt1 = TValueType ("System.DateTime", "5/4/2010 3:02:01 AM"),
+ dto = TValueType ("System.DateTimeOffset", "1/2/2020 3:04:05 AM +04:05"),
+ ts = TValueType ("System.TimeSpan", "3530.00:02:04"),
+ dec = TValueType ("System.Decimal", "123987123"),
+ guid = TValueType ("System.Guid", "3d36e07e-ac90-48c6-b7ec-a481e289d014"),
+ dts = TArray ("System.DateTime[]"),
+ obj = TObject ("DebuggerTests.ClassForToStringTests"),
+ sst = TObject ("DebuggerTests.StructForToStringTests")
+ },
+ "locals#0");
+
+ var dts_elements = await CheckObjectOnLocals (frame_locals, "dts");
+ await CheckDateTime (dts_elements, "[0]", new DateTime (1983, 6, 7, 5, 6, 10));
+ await CheckDateTime (dts_elements, "[1]", new DateTime (1999, 10, 15, 1, 2, 3));
+
+ var obj_props = await CompareObjectPropertiesFor (frame_locals, "obj",
+ new {
+ DT = TValueType ("System.DateTime", "10/15/2004 1:02:03 AM"),
+ DTO = TValueType ("System.DateTimeOffset", "1/2/2020 3:04:05 AM +02:14"),
+ TS = TValueType ("System.TimeSpan", "3530.00:02:04"),
+ Dec = TValueType ("System.Decimal", "1239871"),
+ Guid = TValueType ("System.Guid", "3d36e07e-ac90-48c6-b7ec-a481e289d014")
+ }, "obj_props");
+
+ var sst_props = await CompareObjectPropertiesFor (frame_locals, "sst",
+ new {
+ DT = TValueType ("System.DateTime", "10/15/2004 1:02:03 AM"),
+ DTO = TValueType ("System.DateTimeOffset", "1/2/2020 3:04:05 AM +03:15"),
+ TS = TValueType ("System.TimeSpan", "3530.00:02:04"),
+ Dec = TValueType ("System.Decimal", "1239871"),
+ Guid = TValueType ("System.Guid", "3d36e07e-ac90-48c6-b7ec-a481e289d014")
+ }, "sst_props");
+ });
+ }
+
[Fact]
public async Task EvaluateThisProperties ()
{
@@ -2044,7 +2118,7 @@ namespace DebuggerTests
}
}
- async Task<JToken> CheckObjectOnLocals (JToken locals, string name, Action<JToken> test_fn)
+ async Task<JToken> CheckObjectOnLocals (JToken locals, string name, Action<JToken> test_fn = null)
{
var obj = locals.Where (jt => jt ["name"]?.Value<string> () == name)
.FirstOrDefault ();
@@ -2121,16 +2195,16 @@ namespace DebuggerTests
static JObject TNumber (int value) =>
JObject.FromObject (new { type = "number", value = @value.ToString (), description = value.ToString () });
- static JObject TValueType (string className, object members = null) =>
- JObject.FromObject (new { type = "object", isValueType = true, className = className, description = className });
+ static JObject TValueType (string className, string description = null, object members = null) =>
+ JObject.FromObject (new { type = "object", isValueType = true, className = className, description = description ?? className });
static JObject TEnum (string className, string descr, object members = null) =>
JObject.FromObject (new { type = "object", isEnum = true, className = className, description = descr });
- static JObject TObject (string className, bool is_null = false) =>
+ static JObject TObject (string className, string description = null, bool is_null = false) =>
is_null
- ? JObject.FromObject (new { type = "object", className = className, description = className, subtype = is_null ? "null" : null })
- : JObject.FromObject (new { type = "object", className = className, description = className });
+ ? JObject.FromObject (new { type = "object", className = className, description = description ?? className, subtype = is_null ? "null" : null })
+ : JObject.FromObject (new { type = "object", className = className, description = description ?? className });
static JObject TArray (string className)
=> JObject.FromObject (new { type = "object", className = className, description = className, subtype = "array" });
@@ -2138,6 +2212,9 @@ namespace DebuggerTests
static JObject TBool (bool value)
=> JObject.FromObject (new { type = "boolean", value = @value, description = @value ? "true" : "false" });
+ static JObject TSymbol (string value)
+ => JObject.FromObject (new { type = "symbol", value = @value, description = @value });
+
//TODO add tests covering basic stepping behavior as step in/out/over
}
diff --git a/sdks/wasm/debugger-valuetypes-test.cs b/sdks/wasm/debugger-valuetypes-test.cs
index 58a15478f4d..9d1ee58d0f8 100644
--- a/sdks/wasm/debugger-valuetypes-test.cs
+++ b/sdks/wasm/debugger-valuetypes-test.cs
@@ -91,6 +91,109 @@ namespace DebuggerTests {
public DateTime DT { get; set; }
public RGB RGB;
+
+ public static void MethodWithLocalsForToStringTest (bool call_other)
+ {
+ var dt0 = new DateTime (2020, 1, 2, 3, 4, 5);
+ var dt1 = new DateTime (2010, 5, 4, 3, 2, 1);
+ var ts = dt0 - dt1;
+ var dto = new DateTimeOffset (dt0, new TimeSpan(4, 5, 0));
+ decimal dec = 123987123;
+ var guid = new Guid ("3d36e07e-ac90-48c6-b7ec-a481e289d014");
+
+ var dts = new DateTime [] {
+ new DateTime (1983, 6, 7, 5, 6, 10),
+ new DateTime (1999, 10, 15, 1, 2, 3)
+ };
+
+ var obj = new ClassForToStringTests {
+ DT = new DateTime (2004, 10, 15, 1, 2, 3),
+ DTO = new DateTimeOffset (dt0, new TimeSpan(2, 14, 0)),
+ TS = ts,
+ Dec = 1239871,
+ Guid = guid
+ };
+
+ var sst = new StructForToStringTests {
+ DT = new DateTime (2004, 10, 15, 1, 2, 3),
+ DTO = new DateTimeOffset (dt0, new TimeSpan (3, 15, 0)),
+ TS = ts,
+ Dec = 1239871,
+ Guid = guid
+ };
+ Console.WriteLine ($"MethodWithLocalsForToStringTest: {dt0}, {dt1}, {ts}, {dec}, {guid}, {dts[0]}, {obj.DT}, {sst.DT}");
+ if (call_other)
+ MethodWithArgumentsForToStringTest (call_other, dt0, dt1, ts, dto, dec, guid, dts, obj, sst);
+ }
+
+ static void MethodWithArgumentsForToStringTest (
+ bool call_other, // not really used, just to help with using common code in the tests
+ DateTime dt0, DateTime dt1, TimeSpan ts, DateTimeOffset dto, decimal dec,
+ Guid guid, DateTime[] dts, ClassForToStringTests obj, StructForToStringTests sst)
+ {
+ Console.WriteLine ($"MethodWithArgumentsForToStringTest: {dt0}, {dt1}, {ts}, {dec}, {guid}, {dts[0]}, {obj.DT}, {sst.DT}");
+ }
+
+ public static async Task MethodWithLocalsForToStringTestAsync (bool call_other)
+ {
+ var dt0 = new DateTime (2020, 1, 2, 3, 4, 5);
+ var dt1 = new DateTime (2010, 5, 4, 3, 2, 1);
+ var ts = dt0 - dt1;
+ var dto = new DateTimeOffset (dt0, new TimeSpan(4, 5, 0));
+ decimal dec = 123987123;
+ var guid = new Guid ("3d36e07e-ac90-48c6-b7ec-a481e289d014");
+
+ var dts = new DateTime [] {
+ new DateTime (1983, 6, 7, 5, 6, 10),
+ new DateTime (1999, 10, 15, 1, 2, 3)
+ };
+
+ var obj = new ClassForToStringTests {
+ DT = new DateTime (2004, 10, 15, 1, 2, 3),
+ DTO = new DateTimeOffset (dt0, new TimeSpan(2, 14, 0)),
+ TS = ts,
+ Dec = 1239871,
+ Guid = guid
+ };
+
+ var sst = new StructForToStringTests {
+ DT = new DateTime (2004, 10, 15, 1, 2, 3),
+ DTO = new DateTimeOffset (dt0, new TimeSpan (3, 15, 0)),
+ TS = ts,
+ Dec = 1239871,
+ Guid = guid
+ };
+ Console.WriteLine ($"MethodWithLocalsForToStringTest: {dt0}, {dt1}, {ts}, {dec}, {guid}, {dts[0]}, {obj.DT}, {sst.DT}");
+ if (call_other)
+ await MethodWithArgumentsForToStringTestAsync (call_other, dt0, dt1, ts, dto, dec, guid, dts, obj, sst);
+ }
+
+ static async Task MethodWithArgumentsForToStringTestAsync (
+ bool call_other, // not really used, just to help with using common code in the tests
+ DateTime dt0, DateTime dt1, TimeSpan ts, DateTimeOffset dto, decimal dec,
+ Guid guid, DateTime[] dts, ClassForToStringTests obj, StructForToStringTests sst)
+ {
+ Console.WriteLine ($"MethodWithArgumentsForToStringTest: {dt0}, {dt1}, {ts}, {dec}, {guid}, {dts[0]}, {obj.DT}, {sst.DT}");
+ }
+
+ }
+
+ class ClassForToStringTests
+ {
+ public DateTime DT;
+ public DateTimeOffset DTO;
+ public TimeSpan TS;
+ public decimal Dec;
+ public Guid Guid;
+ }
+
+ struct StructForToStringTests
+ {
+ public DateTime DT;
+ public DateTimeOffset DTO;
+ public TimeSpan TS;
+ public decimal Dec;
+ public Guid Guid;
}
public enum RGB
diff --git a/sdks/wasm/src/library_mono.js b/sdks/wasm/src/library_mono.js
index 65f6d16be32..fdf6fac6f4e 100644
--- a/sdks/wasm/src/library_mono.js
+++ b/sdks/wasm/src/library_mono.js
@@ -470,13 +470,13 @@ var MonoSupportLib = {
MONO._async_method_objectId = objectId;
},
- mono_wasm_begin_value_type_var: function(className) {
+ mono_wasm_begin_value_type_var: function(className, toString) {
fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className));
var vt_obj = {
value: {
type: "object",
className: fixed_class_name,
- description: fixed_class_name,
+ description: (toString == 0 ? fixed_class_name : Module.UTF8ToString (toString)),
// objectId will be generated by MonoProxy
expanded: true,
isValueType: true,
@@ -505,13 +505,13 @@ var MonoSupportLib = {
}
},
- mono_wasm_add_value_type_unexpanded_var: function (className) {
+ mono_wasm_add_value_type_unexpanded_var: function (className, toString) {
fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className));
MONO.var_info.push({
value: {
type: "object",
className: fixed_class_name,
- description: fixed_class_name,
+ description: (toString == 0 ? fixed_class_name : Module.UTF8ToString (toString)),
// objectId added when enumerating object's properties
expanded: false,
isValueType: true
@@ -563,7 +563,7 @@ var MonoSupportLib = {
});
},
- mono_wasm_add_obj_var: function(className, objectId) {
+ mono_wasm_add_obj_var: function(className, toString, objectId) {
if (objectId == 0) {
MONO.mono_wasm_add_null_var (className);
return;
@@ -574,7 +574,7 @@ var MonoSupportLib = {
value: {
type: "object",
className: fixed_class_name,
- description: fixed_class_name,
+ description: (toString == 0 ? fixed_class_name : Module.UTF8ToString (toString)),
objectId: "dotnet:object:"+ objectId,
}
});