[wasm][debugger] Avoid infinite loop when we have a boxed `new object` (#42059)
authorAnkit Jain <radical@gmail.com>
Mon, 14 Sep 2020 19:12:21 +0000 (15:12 -0400)
committerGitHub <noreply@github.com>
Mon, 14 Sep 2020 19:12:21 +0000 (15:12 -0400)
* [wasm][debugger] Avoid infinite loop when we have a boxed `new object`

Eg. `object o = new object(); object o1 = o;`
- Avoid infinitely looping for `o1`

* [wasm][debugger] Handle valuetypes boxed in classes that are not

.. type `object`.

Prompted by @lambdageek's comment - https://github.com/dotnet/runtime/pull/42059#issuecomment-690310274

src/mono/mono/mini/mini-wasm-debugger.c
src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs
src/mono/wasm/debugger/tests/debugger-test.cs

index b978d60..c4f8031 100644 (file)
@@ -928,6 +928,10 @@ describe_value(MonoType * type, gpointer addr, int gpflags)
                        }
 
                        type = m_class_get_byval_arg (klass);
+                       if (type->type == MONO_TYPE_OBJECT) {
+                               mono_wasm_add_obj_var ("object", "object", get_object_id (obj));
+                               break;
+                       }
 
                        // Boxed valuetype
                        if (m_class_is_valuetype (klass))
@@ -965,6 +969,12 @@ describe_value(MonoType * type, gpointer addr, int gpflags)
                        MonoObject *obj = *(MonoObject**)addr;
                        MonoClass *klass = type->data.klass;
 
+                       if (m_class_is_valuetype (mono_object_class (obj))) {
+                               addr = mono_object_unbox_internal (obj);
+                               type = m_class_get_byval_arg (mono_object_class (obj));
+                               goto handle_vtype;
+                       }
+
                        char *class_name = mono_type_full_name (type);
                        int obj_id = get_object_id (obj);
 
index ad3fae9..16296b6 100644 (file)
@@ -1062,6 +1062,57 @@ namespace DebuggerTests
             });
 
         [Theory]
+        [InlineData("BoxedTypeObjectTest", false)]
+        [InlineData("BoxedTypeObjectTestAsync", true)]
+        public async Task InspectBoxedTypeObject(string method_name, bool is_async) => await CheckInspectLocalsAtBreakpointSite(
+            "DebuggerTest",
+            method_name,
+            9,
+            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
+                {
+                    i = TNumber(5),
+                    o0 = TNumber(5),
+                    o1 = TNumber(5),
+                    o2 = TNumber(5),
+                    o3 = TNumber(5),
+
+                    oo = TObject("object"),
+                    oo0 = TObject("object"),
+                }, "locals");
+            });
+
+        [Theory]
+        [InlineData("BoxedAsClass", false)]
+        [InlineData("BoxedAsClassAsync", true)]
+        public async Task InspectBoxedAsClassLocals(string method_name, bool is_async) => await CheckInspectLocalsAtBreakpointSite(
+            "DebuggerTest",
+            method_name,
+            6,
+            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);
+                Console.WriteLine (locals);
+
+                await CheckProps(locals, new
+                {
+                    vt_dt = TDateTime(new DateTime(4819, 5, 6, 7, 8, 9)),
+                    vt_gs = TValueType("Math.GenericStruct<string>"),
+                    e     = TEnum("System.IO.FileMode", "0"),
+                    ee    = TEnum("System.IO.FileMode", "Append")
+                }, "locals");
+            });
+
+
+        [Theory]
         [InlineData(false)]
         [InlineData(true)]
         public async Task InspectValueTypeMethodArgs(bool use_cfo)
index 7de0554..ceb5500 100644 (file)
@@ -382,6 +382,53 @@ public class DebuggerTest
         Console.WriteLine ($"break here");
         await Task.CompletedTask;
     }
+
+    public static void BoxedTypeObjectTest()
+    {
+        int i = 5;
+        object o0 = i;
+        object o1 = o0;
+        object o2 = o1;
+        object o3 = o2;
+
+        object oo = new object();
+        object oo0 = oo;
+        Console.WriteLine ($"break here");
+    }
+    public static async Task BoxedTypeObjectTestAsync()
+    {
+        int i = 5;
+        object o0 = i;
+        object o1 = o0;
+        object o2 = o1;
+        object o3 = o2;
+
+        object oo = new object();
+        object oo0 = oo;
+        Console.WriteLine ($"break here");
+        await Task.CompletedTask;
+    }
+
+    public static void BoxedAsClass()
+    {
+        ValueType vt_dt = new DateTime(4819, 5, 6, 7, 8, 9);
+        ValueType vt_gs = new Math.GenericStruct<string> { StringField = "vt_gs#StringField" };
+        Enum e = new System.IO.FileMode();
+        Enum ee = System.IO.FileMode.Append;
+
+        Console.WriteLine ($"break here");
+    }
+
+    public static async Task BoxedAsClassAsync()
+    {
+        ValueType vt_dt = new DateTime(4819, 5, 6, 7, 8, 9);
+        ValueType vt_gs = new Math.GenericStruct<string> { StringField = "vt_gs#StringField" };
+        Enum e = new System.IO.FileMode();
+        Enum ee = System.IO.FileMode.Append;
+
+        Console.WriteLine ($"break here");
+        await Task.CompletedTask;
+    }
 }
 
 public class MulticastDelegateTestClass