diff options
author | Ankit Jain <radical@gmail.com> | 2020-04-14 05:09:56 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-14 05:09:56 +0300 |
commit | 4eca12e794764da1a0646e77af5b8c60a2e2767a (patch) | |
tree | b5589f91371a3dbfe6c06b78edede4bad94e3ffe | |
parent | 02f1f54f2dc614557c918fcedba47774db5d7b06 (diff) |
[wasm][debugger] Show signature for delegate, or target, if available (#19505)aj-wasm-dbg-improve-vt-descr
* [wasm][debugger] Show signature for delegate, or target, if available
- As object properties, we return a `Target` which has the signature of
the delegate target.
Fixes https://github.com/mono/mono/issues/19382
* [wasm][debugger] Some tidying up
* [wasm][debugger] Remove unused `sig_desc`
* [wasm][debugger] Simplify code, avoid extra allocations
.. as suggested by @lewing
-rw-r--r-- | mono/mini/mini-wasm-debugger.c | 80 | ||||
-rw-r--r-- | sdks/wasm/DebuggerTestSuite/Tests.cs | 606 | ||||
-rw-r--r-- | sdks/wasm/debugger-test.cs | 127 | ||||
-rw-r--r-- | sdks/wasm/src/library_mono.js | 59 |
4 files changed, 635 insertions, 237 deletions
diff --git a/mono/mini/mini-wasm-debugger.c b/mono/mini/mini-wasm-debugger.c index 7dc7cf68e77..5efb34de635 100644 --- a/mono/mini/mini-wasm-debugger.c +++ b/mono/mini/mini-wasm-debugger.c @@ -51,7 +51,8 @@ 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); +extern void mono_wasm_add_func_var (const char*, const char*, guint64); +extern void mono_wasm_add_symbol_var (const char*); extern void mono_wasm_add_array_var (const char*, guint64); extern void mono_wasm_add_properties_var (const char*, gint32); extern void mono_wasm_add_array_item (int); @@ -706,6 +707,28 @@ typedef struct { int *pos; } FrameDescData; +/* + * this returns a string formatted like + * + * <ret_type>:[<comma separated list of arg types>]:<method name> + * + * .. which is consumed by `mono_wasm_add_func_var`. It is used for + * generating this for the delegate, and it's target. + */ +static char* +mono_method_to_desc_for_js (MonoMethod *method, gboolean include_namespace) +{ + MonoMethodSignature *sig = mono_method_signature_internal (method); + char *ret_desc = mono_type_full_name (sig->ret); + char *args_desc = mono_signature_get_desc (sig, include_namespace); + + char *sig_desc = g_strdup_printf ("%s:%s:%s", ret_desc, args_desc, method->name); + + g_free (ret_desc); + g_free (args_desc); + return sig_desc; +} + static guint64 read_enum_value (const char *mem, int type) { @@ -814,10 +837,27 @@ static gboolean describe_value(MonoType * type, gpointer addr, gboolean expandVa char *class_name = mono_type_full_name (type); int obj_id = get_object_id (obj); - if (type->type == MONO_TYPE_SZARRAY || type->type == MONO_TYPE_ARRAY) { - mono_wasm_add_array_var (class_name, obj_id); - } else if (m_class_is_delegate (klass)) { - mono_wasm_add_func_var (class_name, obj_id); + if (type-> type == MONO_TYPE_ARRAY || type->type == MONO_TYPE_SZARRAY) { + mono_wasm_add_array_var(class_name, obj_id); + } else if (m_class_is_delegate (klass) || (type->type == MONO_TYPE_GENERICINST && m_class_is_delegate (type->data.generic_class->container_class))) { + MonoMethod *method; + + if (type->type == MONO_TYPE_GENERICINST) + klass = type->data.generic_class->container_class; + + method = mono_get_delegate_invoke_internal (klass); + if (!method) { + DEBUG_PRINTF (1, "Could not get a method for the delegate for %s\n", class_name); + break; + } + + MonoMethod *tm = ((MonoDelegate *)obj)->method; + char *tm_desc = NULL; + if (tm) + tm_desc = mono_method_to_desc_for_js (tm, FALSE); + + mono_wasm_add_func_var (class_name, tm_desc, obj_id); + g_free (tm_desc); } else { char *to_string_val = get_to_string_description (class_name, klass, addr); mono_wasm_add_obj_var (class_name, to_string_val, obj_id); @@ -993,6 +1033,28 @@ describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAs } } +/* + * We return a `Target` property only for now. + * In future, we could add a `MethodInfo` too. + */ +static gboolean +describe_delegate_properties (MonoObject *obj) +{ + MonoClass *klass = mono_object_class(obj); + if (!m_class_is_delegate (klass)) + return FALSE; + + // Target, like in VS - what is this field supposed to be, anyway?? + MonoMethod *tm = ((MonoDelegate *)obj)->method; + char * sig_desc = mono_method_to_desc_for_js (tm, FALSE); + + mono_wasm_add_properties_var ("Target", -1); + mono_wasm_add_func_var (NULL, sig_desc, -1); + + g_free (sig_desc); + return TRUE; +} + static gboolean describe_object_properties (guint64 objectId, gboolean isAsyncLocalThis, gboolean expandValueType) { @@ -1009,7 +1071,13 @@ describe_object_properties (guint64 objectId, gboolean isAsyncLocalThis, gboolea return FALSE; } - describe_object_properties_for_klass (obj, obj->vtable->klass, isAsyncLocalThis, expandValueType); + if (m_class_is_delegate (mono_object_class (obj))) { + // delegates get the same id format as regular objects + describe_delegate_properties (obj); + } else { + describe_object_properties_for_klass (obj, obj->vtable->klass, isAsyncLocalThis, expandValueType); + } + return TRUE; } diff --git a/sdks/wasm/DebuggerTestSuite/Tests.cs b/sdks/wasm/DebuggerTestSuite/Tests.cs index 5640f713bf1..e781755954e 100644 --- a/sdks/wasm/DebuggerTestSuite/Tests.cs +++ b/sdks/wasm/DebuggerTestSuite/Tests.cs @@ -332,21 +332,6 @@ namespace DebuggerTests Assert.True(false, $"Could not find variable '{name}'"); } - void CheckFunction (JToken locals, string name, string description, string subtype=null) { - Console.WriteLine ($"** Locals: {locals.ToString ()}"); - foreach (var l in locals) { - if (name != l["name"]?.Value<string> ()) - continue; - - var val = l["value"]; - Assert.Equal ("function", val ["type"]?.Value<string> ()); - Assert.Equal (description, val ["description"]?.Value<string> ()); - Assert.Equal (subtype, val ["subtype"]?.Value<string> ()); - return; - } - Assert.True(false, $"Could not find variable '{name}'"); - } - JToken GetAndAssertObjectWithName (JToken obj, string name) { var l = obj.FirstOrDefault (jt => jt ["name"]?.Value<string> () == name); @@ -385,22 +370,29 @@ namespace DebuggerTests await CheckInspectLocalsAtBreakpointSite ( "dotnet://debugger-test.dll/debugger-test.cs", 41, 2, "DelegatesTest", "window.setTimeout(function() { invoke_delegates_test (); }, 1);", - test_fn: (locals) => { - CheckObject (locals, "fn_func", "System.Func<Math, bool>"); - CheckObject (locals, "fn_func_null", "System.Func<Math, bool>", is_null: true); - CheckArray (locals, "fn_func_arr", "System.Func<Math, bool>[]"); - CheckFunction (locals, "fn_del", "Math.IsMathNull"); - CheckObject (locals, "fn_del_null", "Math.IsMathNull", is_null: true); - CheckArray (locals, "fn_del_arr", "Math.IsMathNull[]"); - - // Unused locals - CheckObject (locals, "fn_func_unused", "System.Func<Math, bool>", is_null: true); - CheckObject (locals, "fn_func_null_unused", "System.Func<Math, bool>", is_null: true); - CheckObject (locals, "fn_func_arr_unused", "System.Func<Math, bool>[]", is_null: true); - - CheckObject (locals, "fn_del_unused", "Math.IsMathNull", is_null: true); - CheckObject (locals, "fn_del_null_unused", "Math.IsMathNull", is_null: true); - CheckObject (locals, "fn_del_arr_unused", "Math.IsMathNull[]", is_null: true); + wait_for_event_fn: async (pause_location) => { + var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value<string>()); + + await CheckProps (locals, new { + fn_func = TDelegate ("System.Func<Math, bool>", "bool <DelegatesTest>|(Math)"), + fn_func_null = TObject ("System.Func<Math, bool>", is_null: true), + fn_func_arr = TArray ("System.Func<Math, bool>[]"), + fn_del = TDelegate ("Math.IsMathNull", "bool IsMathNullDelegateTarget (Math)"), + fn_del_null = TObject ("Math.IsMathNull", is_null: true), + fn_del_arr = TArray ("Math.IsMathNull[]"), + + // Unused locals + fn_func_unused = TObject ("System.Func<Math, bool>", is_null: true), + fn_func_null_unused = TObject ("System.Func<Math, bool>", is_null: true), + fn_func_arr_unused = TObject ("System.Func<Math, bool>[]", is_null: true), + + fn_del_unused = TObject ("Math.IsMathNull", is_null: true), + fn_del_null_unused = TObject ("Math.IsMathNull", is_null: true), + fn_del_arr_unused = TObject ("Math.IsMathNull[]", is_null: true), + + res = TBool (false), + m_obj = TObject ("Math") + }, "locals"); } ); @@ -425,7 +417,202 @@ namespace DebuggerTests } ); - async Task CheckInspectLocalsAtBreakpointSite (string url_key, int line, int column, string function_name, string eval_expression, Action<JToken> test_fn) { + [Fact] + public async Task InspectDelegateSignaturesWithFunc () + => await CheckInspectLocalsAtBreakpointSite ( + "dotnet://debugger-test.dll/debugger-test.cs", + 185, 2, + "DelegatesSignatureTest", + "window.setTimeout (function () { invoke_static_method ('[debugger-test] Math:DelegatesSignatureTest'); }, 1)", + wait_for_event_fn: async (pause_location) => { + var locals = await GetProperties (pause_location ["callFrames"][0]["callFrameId"].Value<string>()); + + await CheckProps (locals, new { + fn_func = TDelegate ("System.Func<Math, Math.GenericStruct<Math.GenericStruct<int[]>>, Math.GenericStruct<bool[]>>", + "Math.GenericStruct<bool[]> <DelegatesSignatureTest>|(Math,Math.GenericStruct<Math.GenericStruct<int[]>>)"), + + fn_func_del = TDelegate ("System.Func<Math, Math.GenericStruct<Math.GenericStruct<int[]>>, Math.GenericStruct<bool[]>>", + "Math.GenericStruct<bool[]> DelegateTargetForSignatureTest (Math,Math.GenericStruct<Math.GenericStruct<int[]>>)"), + + fn_func_null = TObject ("System.Func<Math, Math.GenericStruct<Math.GenericStruct<int[]>>, Math.GenericStruct<bool[]>>", is_null: true), + fn_func_only_ret= TDelegate ("System.Func<bool>", "bool <DelegatesSignatureTest>|()"), + fn_func_arr = TArray ("System.Func<Math, Math.GenericStruct<Math.GenericStruct<int[]>>, Math.GenericStruct<bool[]>>[]"), + + fn_del = TDelegate ("Math.DelegateForSignatureTest", + "Math.GenericStruct<bool[]> DelegateTargetForSignatureTest (Math,Math.GenericStruct<Math.GenericStruct<int[]>>)"), + + fn_del_l = TDelegate ("Math.DelegateForSignatureTest", + "Math.GenericStruct<bool[]> <DelegatesSignatureTest>|(Math,Math.GenericStruct<Math.GenericStruct<int[]>>)"), + + fn_del_null = TObject ("Math.DelegateForSignatureTest", is_null: true), + fn_del_arr = TArray ("Math.DelegateForSignatureTest[]"), + m_obj = TObject ("Math"), + gs_gs = TValueType ("Math.GenericStruct<Math.GenericStruct<int[]>>"), + fn_void_del = TDelegate ("Math.DelegateWithVoidReturn", + "void DelegateTargetWithVoidReturn (Math.GenericStruct<int[]>)"), + + fn_void_del_arr = TArray ("Math.DelegateWithVoidReturn[]"), + fn_void_del_null= TObject ("Math.DelegateWithVoidReturn", is_null: true), + gs = TValueType ("Math.GenericStruct<int[]>"), + rets = TArray ("Math.GenericStruct<bool[]>[]") + }, "locals"); + + await CompareObjectPropertiesFor (locals, "fn_func_arr", new [] { + TDelegate ( + "System.Func<Math, Math.GenericStruct<Math.GenericStruct<int[]>>, Math.GenericStruct<bool[]>>", + "Math.GenericStruct<bool[]> <DelegatesSignatureTest>|(Math,Math.GenericStruct<Math.GenericStruct<int[]>>)"), + }, "locals#fn_func_arr"); + + await CompareObjectPropertiesFor (locals, "fn_del_arr", new [] { + TDelegate ( + "Math.DelegateForSignatureTest", + "Math.GenericStruct<bool[]> DelegateTargetForSignatureTest (Math,Math.GenericStruct<Math.GenericStruct<int[]>>)"), + TDelegate ( + "Math.DelegateForSignatureTest", + "Math.GenericStruct<bool[]> <DelegatesSignatureTest>|(Math,Math.GenericStruct<Math.GenericStruct<int[]>>)") + }, "locals#fn_del_arr"); + + await CompareObjectPropertiesFor (locals, "fn_void_del_arr", new [] { + TDelegate ( + "Math.DelegateWithVoidReturn", + "void DelegateTargetWithVoidReturn (Math.GenericStruct<int[]>)") + }, "locals#fn_void_del_arr"); + }); + + [Fact] + public async Task ActionTSignatureTest () + => await CheckInspectLocalsAtBreakpointSite ( + "dotnet://debugger-test.dll/debugger-test.cs", 206, 2, + "ActionTSignatureTest", + "window.setTimeout (function () { invoke_static_method ('[debugger-test] Math:ActionTSignatureTest'); }, 1)", + wait_for_event_fn: async (pause_location) => { + var locals = await GetProperties (pause_location ["callFrames"][0]["callFrameId"].Value<string>()); + + await CheckProps (locals, new + { + fn_action = TDelegate ("System.Action<Math.GenericStruct<int[]>>", + "void <ActionTSignatureTest>|(Math.GenericStruct<int[]>)"), + fn_action_del = TDelegate ("System.Action<Math.GenericStruct<int[]>>", + "void DelegateTargetWithVoidReturn (Math.GenericStruct<int[]>)"), + fn_action_bare = TDelegate ("System.Action", + "void|()"), + + fn_action_null = TObject ("System.Action<Math.GenericStruct<int[]>>", is_null: true), + + fn_action_arr = TArray ("System.Action<Math.GenericStruct<int[]>>[]"), + + gs = TValueType ("Math.GenericStruct<int[]>"), + }, "locals"); + + await CompareObjectPropertiesFor (locals, "fn_action_arr", new [] { + TDelegate ( + "System.Action<Math.GenericStruct<int[]>>", + "void <ActionTSignatureTest>|(Math.GenericStruct<int[]>)"), + TDelegate ( + "System.Action<Math.GenericStruct<int[]>>", + "void DelegateTargetWithVoidReturn (Math.GenericStruct<int[]>)"), + TObject ("System.Action<Math.GenericStruct<int[]>>", is_null: true) + }, "locals#fn_action_arr"); + }); + + [Fact] + public async Task NestedDelegatesTest () + => await CheckInspectLocalsAtBreakpointSite ( + "dotnet://debugger-test.dll/debugger-test.cs", 225, 2, + "NestedDelegatesTest", + "window.setTimeout (function () { invoke_static_method ('[debugger-test] Math:NestedDelegatesTest'); }, 1)", + wait_for_event_fn: async (pause_location) => { + var locals = await GetProperties (pause_location ["callFrames"][0]["callFrameId"].Value<string>()); + + await CheckProps (locals, new { + fn_func = TDelegate ("System.Func<System.Func<int, bool>, bool>", + "bool <NestedDelegatesTest>|(Func<int, bool>)"), + fn_func_null = TObject ("System.Func<System.Func<int, bool>, bool>", is_null: true), + fn_func_arr = TArray ("System.Func<System.Func<int, bool>, bool>[]"), + fn_del_arr = TArray ("System.Func<System.Func<int, bool>, bool>[]"), + + m_obj = TObject ("Math"), + fn_del_null = TObject ("System.Func<System.Func<int, bool>, bool>", is_null: true), + fs = TDelegate ("System.Func<int, bool>", + "bool <NestedDelegatesTest>|(int)") + }, "locals"); + + await CompareObjectPropertiesFor (locals, "fn_func_arr", new [] { + TDelegate ( + "System.Func<System.Func<int, bool>, bool>", + "bool <NestedDelegatesTest>|(System.Func<int, bool>)") + }, "locals#fn_func_arr"); + + await CompareObjectPropertiesFor (locals, "fn_del_arr", new [] { + TDelegate ( + "System.Func<System.Func<int, bool>, bool>", + "bool DelegateTargetForNestedFunc (Func<int, bool>)") + }, "locals#fn_del_arr"); + }); + + [Fact] + public async Task DelegatesAsMethodArgsTest () + => await CheckInspectLocalsAtBreakpointSite ( + "dotnet://debugger-test.dll/debugger-test.cs", 244, 2, + "MethodWithDelegateArgs", + "window.setTimeout (function () { invoke_static_method ('[debugger-test] Math:DelegatesAsMethodArgsTest'); }, 1)", + wait_for_event_fn: async (pause_location) => { + var locals = await GetProperties (pause_location ["callFrames"][0]["callFrameId"].Value<string>()); + + await CheckProps (locals, new { + @this = TObject ("Math"), + dst_arr = TArray ("Math.DelegateForSignatureTest[]"), + fn_func = TDelegate ("System.Func<char[], bool>", + "bool <DelegatesAsMethodArgsTest>|(char[])"), + fn_action = TDelegate ("System.Action<Math.GenericStruct<int>[]>", + "void <DelegatesAsMethodArgsTest>|(Math.GenericStruct<int>[])") + }, "locals"); + + await CompareObjectPropertiesFor (locals, "dst_arr", new [] { + TDelegate ("Math.DelegateForSignatureTest", + "Math.GenericStruct<bool[]> DelegateTargetForSignatureTest (Math,Math.GenericStruct<Math.GenericStruct<int[]>>)"), + TDelegate ("Math.DelegateForSignatureTest", + "Math.GenericStruct<bool[]> <DelegatesAsMethodArgsTest>|(Math,Math.GenericStruct<Math.GenericStruct<int[]>>)"), + }, "locals#dst_arr"); + }); + + [Fact] + public async Task MethodWithDelegatesAsyncTest () + => await CheckInspectLocalsAtBreakpointSite ( + "dotnet://debugger-test.dll/debugger-test.cs", 262, 2, + "MoveNext", //"DelegatesAsMethodArgsTestAsync" + "window.setTimeout (function () { invoke_static_method_async ('[debugger-test] Math:MethodWithDelegatesAsyncTest'); }, 1)", + wait_for_event_fn: async (pause_location) => { + var locals = await GetProperties (pause_location ["callFrames"][0]["callFrameId"].Value<string>()); + + await CheckProps (locals, new { + @this = TObject ("Math"), + _dst_arr = TArray ("Math.DelegateForSignatureTest[]"), + _fn_func = TDelegate ("System.Func<char[], bool>", + "bool <MethodWithDelegatesAsync>|(char[])"), + _fn_action = TDelegate ("System.Action<Math.GenericStruct<int>[]>", + "void <MethodWithDelegatesAsync>|(Math.GenericStruct<int>[])") + }, "locals"); + + await CompareObjectPropertiesFor (locals, "_dst_arr", new [] { + TDelegate ( + "Math.DelegateForSignatureTest", + "Math.GenericStruct<bool[]> DelegateTargetForSignatureTest (Math,Math.GenericStruct<Math.GenericStruct<int[]>>)"), + TDelegate ( + "Math.DelegateForSignatureTest", + "Math.GenericStruct<bool[]> <MethodWithDelegatesAsync>|(Math,Math.GenericStruct<Math.GenericStruct<int[]>>)"), + }, "locals#dst_arr"); + }); + + object TGenericStruct(string typearg, string stringField) + => new { + List = TObject ($"System.Collections.Generic.List<{typearg}>"), + StringField = TString (stringField) + }; + + async Task CheckInspectLocalsAtBreakpointSite (string url_key, int line, int column, string function_name, string eval_expression, + Action<JToken> test_fn = null, Func<JObject, Task> wait_for_event_fn = null) + { var insp = new Inspector (); //Collect events var scripts = SubscribeToScripts(insp); @@ -439,7 +626,7 @@ namespace DebuggerTests await EvaluateAndCheck ( eval_expression, url_key, line, column, function_name, - wait_for_event_fn: (pause_location) => { + wait_for_event_fn: async (pause_location) => { //make sure we're on the right bp Assert.Equal (bp.Value ["breakpointId"]?.ToString (), pause_location ["hitBreakpoints"]?[0]?.Value<string> ()); @@ -448,7 +635,10 @@ namespace DebuggerTests var scope = top_frame ["scopeChain"][0]; Assert.Equal ("dotnet:scope:0", scope ["object"]["objectId"]); - return Task.CompletedTask; + if (wait_for_event_fn != null) + await wait_for_event_fn(pause_location); + else + await Task.CompletedTask; }, locals_fn: (locals) => { if (test_fn != null) @@ -1777,203 +1967,89 @@ namespace DebuggerTests [Fact] public async Task EvaluateThisProperties () - { - var insp = new Inspector (); - //Collect events - var scripts = SubscribeToScripts(insp); - int line = 18; - int col = 16; - string entry_method_name = "[debugger-test] DebuggerTests.EvaluateTestsClass:EvaluateLocals"; - - await Ready(); - await insp.Ready (async (cli, token) => { - ctx = new DebugTestContext (cli, insp, token, scripts); - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-evaluate-test.cs"; - - await SetBreakpoint (debugger_test_loc, line, col); - - var eval_expr = "window.setTimeout(function() { invoke_static_method_async (" - + $"'{entry_method_name}'" - + "); }, 1);"; - - var pause_location = await EvaluateAndCheck (eval_expr, - debugger_test_loc, - line, - col, - "run", - wait_for_event_fn: async (pause_location) => { - var locals = await GetProperties (pause_location ["callFrames"][0] ["callFrameId"].Value<string> ()); - var evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "a"); - CheckContentValue (evaluate, "1"); - evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "b"); - CheckContentValue (evaluate, "2"); - evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "c"); - CheckContentValue (evaluate, "3"); - }); - }); - } + => await CheckInspectLocalsAtBreakpointSite ( + "dotnet://debugger-test.dll/debugger-evaluate-test.cs", 18, 16, + "run", + "window.setTimeout(function() { invoke_static_method_async ('[debugger-test] DebuggerTests.EvaluateTestsClass:EvaluateLocals'); })", + wait_for_event_fn: async (pause_location) => { + var locals = await GetProperties (pause_location ["callFrames"][0] ["callFrameId"].Value<string> ()); + var evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "a"); + CheckContentValue (evaluate, "1"); + evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "b"); + CheckContentValue (evaluate, "2"); + evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "c"); + CheckContentValue (evaluate, "3"); + }); [Fact] public async Task EvaluateParameters () - { - var insp = new Inspector (); - //Collect events - var scripts = SubscribeToScripts(insp); - int line = 18; - int col = 16; - string entry_method_name = "[debugger-test] DebuggerTests.EvaluateTestsClass:EvaluateLocals"; - - await Ready(); - await insp.Ready (async (cli, token) => { - ctx = new DebugTestContext (cli, insp, token, scripts); - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-evaluate-test.cs"; - - await SetBreakpoint (debugger_test_loc, line, col); - - var eval_expr = "window.setTimeout(function() { invoke_static_method_async (" - + $"'{entry_method_name}'" - + "); }, 1);"; - - var pause_location = await EvaluateAndCheck (eval_expr, - debugger_test_loc, - line, - col, - "run", - wait_for_event_fn: async (pause_location) => { - var locals = await GetProperties (pause_location ["callFrames"][0] ["callFrameId"].Value<string> ()); - var evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "g"); - CheckContentValue (evaluate, "100"); - evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "h"); - CheckContentValue (evaluate, "200"); - evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "valString"); - CheckContentValue (evaluate, "test"); - }); - }); - } + => await CheckInspectLocalsAtBreakpointSite ( + "dotnet://debugger-test.dll/debugger-evaluate-test.cs", 18, 16, + "run", + "window.setTimeout(function() { invoke_static_method_async ('[debugger-test] DebuggerTests.EvaluateTestsClass:EvaluateLocals'); })", + wait_for_event_fn: async (pause_location) => { + var locals = await GetProperties (pause_location ["callFrames"][0] ["callFrameId"].Value<string> ()); + var evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "g"); + CheckContentValue (evaluate, "100"); + evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "h"); + CheckContentValue (evaluate, "200"); + evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "valString"); + CheckContentValue (evaluate, "test"); + }); [Fact] public async Task EvaluateLocals () - { - var insp = new Inspector (); - //Collect events - var scripts = SubscribeToScripts(insp); - int line = 18; - int col = 16; - string entry_method_name = "[debugger-test] DebuggerTests.EvaluateTestsClass:EvaluateLocals"; - - await Ready(); - await insp.Ready (async (cli, token) => { - ctx = new DebugTestContext (cli, insp, token, scripts); - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-evaluate-test.cs"; - - await SetBreakpoint (debugger_test_loc, line, col); - - var eval_expr = "window.setTimeout(function() { invoke_static_method_async (" - + $"'{entry_method_name}'" - + "); }, 1);"; - - var pause_location = await EvaluateAndCheck (eval_expr, - debugger_test_loc, - line, - col, - "run", - wait_for_event_fn: async (pause_location) => { - var locals = await GetProperties (pause_location ["callFrames"][0] ["callFrameId"].Value<string> ()); - var evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "d"); - CheckContentValue (evaluate, "101"); - evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "e"); - CheckContentValue (evaluate, "102"); - evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "f"); - CheckContentValue (evaluate, "103"); - }); - }); - } - + => await CheckInspectLocalsAtBreakpointSite ( + "dotnet://debugger-test.dll/debugger-evaluate-test.cs", 18, 16, + "run", + "window.setTimeout(function() { invoke_static_method_async ('[debugger-test] DebuggerTests.EvaluateTestsClass:EvaluateLocals'); })", + wait_for_event_fn: async (pause_location) => { + var locals = await GetProperties (pause_location ["callFrames"][0] ["callFrameId"].Value<string> ()); + var evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "d"); + CheckContentValue (evaluate, "101"); + evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "e"); + CheckContentValue (evaluate, "102"); + evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "f"); + CheckContentValue (evaluate, "103"); + }); [Fact] public async Task EvaluateExpressions () - { - var insp = new Inspector (); - //Collect events - var scripts = SubscribeToScripts(insp); - int line = 18; - int col = 16; - string entry_method_name = "[debugger-test] DebuggerTests.EvaluateTestsClass:EvaluateLocals"; - - await Ready(); - await insp.Ready (async (cli, token) => { - ctx = new DebugTestContext (cli, insp, token, scripts); - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-evaluate-test.cs"; - - await SetBreakpoint (debugger_test_loc, line, col); - - var eval_expr = "window.setTimeout(function() { invoke_static_method_async (" - + $"'{entry_method_name}'" - + "); }, 1);"; - - var pause_location = await EvaluateAndCheck (eval_expr, - debugger_test_loc, - line, - col, - "run", - wait_for_event_fn: async (pause_location) => { - var locals = await GetProperties (pause_location ["callFrames"][0] ["callFrameId"].Value<string> ()); - var evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "d + e"); - CheckContentValue (evaluate, "203"); - evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "e + 10"); - CheckContentValue (evaluate, "112"); - evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "a + a"); - CheckContentValue (evaluate, "2"); - evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "this.a + this.b"); - CheckContentValue (evaluate, "3"); - evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "\"test\" + \"test\""); - CheckContentValue (evaluate, "testtest"); - evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "5 + 5"); - CheckContentValue (evaluate, "10"); - }); - }); - } + => await CheckInspectLocalsAtBreakpointSite ( + "dotnet://debugger-test.dll/debugger-evaluate-test.cs", 18, 16, + "run", + "window.setTimeout(function() { invoke_static_method_async ('[debugger-test] DebuggerTests.EvaluateTestsClass:EvaluateLocals'); })", + wait_for_event_fn: async (pause_location) => { + var locals = await GetProperties (pause_location ["callFrames"][0] ["callFrameId"].Value<string> ()); + var evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "d + e"); + CheckContentValue (evaluate, "203"); + evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "e + 10"); + CheckContentValue (evaluate, "112"); + evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "a + a"); + CheckContentValue (evaluate, "2"); + evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "this.a + this.b"); + CheckContentValue (evaluate, "3"); + evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "\"test\" + \"test\""); + CheckContentValue (evaluate, "testtest"); + evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "5 + 5"); + CheckContentValue (evaluate, "10"); + }); [Fact] public async Task EvaluateThisExpressions () - { - var insp = new Inspector (); - //Collect events - var scripts = SubscribeToScripts(insp); - int line = 18; - int col = 16; - string entry_method_name = "[debugger-test] DebuggerTests.EvaluateTestsClass:EvaluateLocals"; - - await Ready(); - await insp.Ready (async (cli, token) => { - ctx = new DebugTestContext (cli, insp, token, scripts); - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-evaluate-test.cs"; - - await SetBreakpoint (debugger_test_loc, line, col); - - var eval_expr = "window.setTimeout(function() { invoke_static_method_async (" - + $"'{entry_method_name}'" - + "); }, 1);"; - - var pause_location = await EvaluateAndCheck (eval_expr, - debugger_test_loc, - line, - col, - "run", - wait_for_event_fn: async (pause_location) => { - var locals = await GetProperties (pause_location ["callFrames"][0] ["callFrameId"].Value<string> ()); - var evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "this.a"); - CheckContentValue (evaluate, "1"); - evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "this.b"); - CheckContentValue (evaluate, "2"); - evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "this.c"); - CheckContentValue (evaluate, "3"); - }); - }); - } - - - + => await CheckInspectLocalsAtBreakpointSite ( + "dotnet://debugger-test.dll/debugger-evaluate-test.cs", 18, 16, + "run", + "window.setTimeout(function() { invoke_static_method_async ('[debugger-test] DebuggerTests.EvaluateTestsClass:EvaluateLocals'); })", + wait_for_event_fn: async (pause_location) => { + var locals = await GetProperties (pause_location ["callFrames"][0] ["callFrameId"].Value<string> ()); + var evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "this.a"); + CheckContentValue (evaluate, "1"); + evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "this.b"); + CheckContentValue (evaluate, "2"); + evaluate = await EvaluateOnCallFrame (pause_location ["callFrames"][0] ["callFrameId"].Value<string> (), "this.c"); + CheckContentValue (evaluate, "3"); + }); async Task<JObject> StepAndCheck (StepKind kind, string script_loc, int line, int column, string function_name, Func<JObject, Task> wait_for_event_fn = null, Action<JToken> locals_fn = null, int times=1) @@ -2023,6 +2099,65 @@ namespace DebuggerTests return wait_res; } + async Task CheckDelegate (JToken locals, string name, string className, string target) + { + var l = GetAndAssertObjectWithName (locals, name); + var val = l["value"]; + + await CheckDelegate (l, TDelegate (className, target), name); + } + + async Task CheckDelegate (JToken actual_val, JToken exp_val, string label) + { + AssertEqual ("object", actual_val["type"]?.Value<string>(), $"{label}-type"); + AssertEqual (exp_val ["className"]?.Value<string> (), actual_val ["className"]?.Value<string>(), $"{label}-className"); + + var actual_target = actual_val["description"]?.Value<string>(); + Assert.True(actual_target != null, $"${label}-description"); + var exp_target = exp_val["target"].Value<string>(); + + CheckDelegateTarget (actual_target, exp_target); + + var del_props = await GetProperties(actual_val["objectId"]?.Value<string>()); + AssertEqual (1, del_props.Count(), $"${label}-delegate-properties-count"); + + var obj = del_props.Where (jt => jt ["name"]?.Value<string> () == "Target").FirstOrDefault (); + Assert.True (obj != null, $"[{label}] Property named 'Target' found found in delegate properties"); + + AssertEqual("symbol", obj ["value"]?["type"]?.Value<string>(), $"{label}#Target#type"); + CheckDelegateTarget(obj ["value"]?["value"]?.Value<string>(), exp_target); + + return; + + void CheckDelegateTarget(string actual_target, string exp_target) + { + var parts = exp_target.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 1) { + // not a generated method + AssertEqual(exp_target, actual_target, $"{label}-description"); + } else { + bool prefix = actual_target.StartsWith(parts[0], StringComparison.Ordinal); + Assert.True(prefix, $"{label}-description, Expected target to start with '{parts[0]}'. Actual: '{actual_target}'"); + + var remaining = actual_target.Substring(parts[0].Length); + bool suffix = remaining.EndsWith(parts[1], StringComparison.Ordinal); + Assert.True(prefix, $"{label}-description, Expected target to end with '{parts[1]}'. Actual: '{remaining}'"); + } + } + } + + async Task CheckCustomType (JToken actual_val, JToken exp_val, string label) + { + var ctype = exp_val["__custom_type"].Value<string>(); + switch (ctype) { + case "delegate": + await CheckDelegate (actual_val, exp_val, label); + break; + default: + throw new ArgumentException($"{ctype} not supported"); + } + } + async Task CheckProps (JToken actual, object exp_o, string label, int num_fields=-1) { if (exp_o.GetType ().IsArray || exp_o is JArray) { @@ -2032,7 +2167,7 @@ namespace DebuggerTests } var exp_v_arr = JArray.FromObject (exp_o); - Assert.Equal (exp_v_arr.Count, actual_arr.Count ()); + AssertEqual (exp_v_arr.Count, actual_arr.Count (), $"{label}-count"); for (int i = 0; i < exp_v_arr.Count; i ++) { var exp_i = exp_v_arr [i]; @@ -2078,6 +2213,11 @@ namespace DebuggerTests async Task CheckValue (JToken actual_val, JToken exp_val, string label) { + if (exp_val ["__custom_type"] != null) { + await CheckCustomType (actual_val, exp_val, label); + return; + } + if (exp_val ["type"] == null && actual_val ["objectId"] != null) { var new_val = await GetProperties (actual_val ["objectId"].Value<string> ()); await CheckProps (new_val, exp_val, $"{label}-{actual_val["objectId"]?.Value<string>()}"); @@ -2085,6 +2225,13 @@ namespace DebuggerTests } foreach (var jp in exp_val.Values<JProperty> ()) { + if (jp.Value.Type == JTokenType.Object) { + var new_val = await GetProperties (actual_val ["objectId"].Value<string> ()); + await CheckProps (new_val, jp.Value, $"{label}-{actual_val["objectId"]?.Value<string>()}"); + + continue; + } + var exp_val_str = jp.Value.Value<string> (); bool null_or_empty_exp_val = String.IsNullOrEmpty (exp_val_str); @@ -2237,6 +2384,12 @@ namespace DebuggerTests return bp1_res; } + void AssertEqual (object expected, object actual, string label) + => Assert.True (expected?.Equals (actual), + $"[{label}]\n" + + $"Expected: {expected?.ToString()}\n" + + $"Actual: {actual?.ToString()}\n"); + //FIXME: um maybe we don't need to convert jobject right here! static JObject TString (string value) => value == null @@ -2266,6 +2419,19 @@ namespace DebuggerTests static JObject TSymbol (string value) => JObject.FromObject (new { type = "symbol", value = @value, description = @value }); + /* + For target names with generated method names like + `void <ActionTSignatureTest>b__11_0 (Math.GenericStruct<int[]>)` + + .. pass target "as `target: "void <ActionTSignatureTest>|(Math.GenericStruct<int[]>)"` + */ + static JObject TDelegate(string className, string target) + => JObject.FromObject(new { + __custom_type = "delegate", + className = className, + target = target + }); + //TODO add tests covering basic stepping behavior as step in/out/over } diff --git a/sdks/wasm/debugger-test.cs b/sdks/wasm/debugger-test.cs index 118320ae91c..be77e8c57e6 100644 --- a/sdks/wasm/debugger-test.cs +++ b/sdks/wasm/debugger-test.cs @@ -145,6 +145,130 @@ public class Math { //Only append content to this class as the test suite depend Console.WriteLine ($"{c0}, {c1}"); } + public static int DelegatesSignatureTest () + { + Func<Math, GenericStruct<GenericStruct<int[]>>, GenericStruct<bool[]>> fn_func = (m, gs) => new GenericStruct<bool[]>(); + Func<Math, GenericStruct<GenericStruct<int[]>>, GenericStruct<bool[]>> fn_func_del = GenericStruct<int>.DelegateTargetForSignatureTest; + Func<Math, GenericStruct<GenericStruct<int[]>>, GenericStruct<bool[]>> fn_func_null = null; + Func<bool> fn_func_only_ret = () => { Console.WriteLine ($"hello"); return true; }; + var fn_func_arr = new Func<Math, GenericStruct<GenericStruct<int[]>>, GenericStruct<bool[]>>[] { (m, gs) => new GenericStruct<bool[]>() }; + + Math.DelegateForSignatureTest fn_del = GenericStruct<int>.DelegateTargetForSignatureTest; + Math.DelegateForSignatureTest fn_del_l = (m, gs) => new GenericStruct<bool[]> { StringField = "fn_del_l#lambda" }; + var fn_del_arr = new Math.DelegateForSignatureTest[] { GenericStruct<int>.DelegateTargetForSignatureTest, (m, gs) => new GenericStruct<bool[]> { StringField = "fn_del_arr#1#lambda" } }; + var m_obj = new Math (); + Math.DelegateForSignatureTest fn_del_null = null; + var gs_gs = new GenericStruct<GenericStruct<int[]>> + { + List = new System.Collections.Generic.List<GenericStruct<int[]>> { + new GenericStruct<int[]> { StringField = "gs#List#0#StringField" }, + new GenericStruct<int[]> { StringField = "gs#List#1#StringField" } + } + }; + + Math.DelegateWithVoidReturn fn_void_del = Math.DelegateTargetWithVoidReturn; + var fn_void_del_arr = new Math.DelegateWithVoidReturn[] { Math.DelegateTargetWithVoidReturn }; + Math.DelegateWithVoidReturn fn_void_del_null = null; + + var rets = new GenericStruct<bool[]>[] { + fn_func (m_obj, gs_gs), + fn_func_del (m_obj, gs_gs), + fn_del (m_obj, gs_gs), + fn_del_l (m_obj, gs_gs), + fn_del_arr[0] (m_obj, gs_gs), + fn_func_arr[0] (m_obj, gs_gs) + }; + + var gs = new GenericStruct<int[]>(); + fn_void_del (gs); + fn_void_del_arr[0](gs); + fn_func_only_ret (); + foreach (var ret in rets) Console.WriteLine ($"ret: {ret}"); + Console.WriteLine ($"- {gs_gs.List[0].StringField}"); + Console.WriteLine ("DelegatesSignatureTest: Just a test message, ignore"); + return 0; + } + + public static int ActionTSignatureTest () + { + Action<GenericStruct<int[]>> fn_action = (_) => { }; + Action<GenericStruct<int[]>> fn_action_del = Math.DelegateTargetWithVoidReturn; + Action fn_action_bare = () => {}; + Action<GenericStruct<int[]>> fn_action_null = null; + var fn_action_arr = new Action<GenericStruct<int[]>>[] { + (gs) => new GenericStruct<int[]>(), + Math.DelegateTargetWithVoidReturn, + null + }; + + var gs = new GenericStruct<int[]>(); + fn_action (gs); + fn_action_del (gs); + fn_action_arr[0](gs); + fn_action_bare (); + Console.WriteLine ("DelegatesSignatureTest: Just a test message, ignore"); + return 0; + } + + public static int NestedDelegatesTest () + { + Func<Func<int, bool>, bool> fn_func = (_) => { return true; }; + Func<Func<int, bool>, bool> fn_func_null = null; + var fn_func_arr = new Func<Func<int, bool>, bool>[] { (gs) => { return true; } }; + + var fn_del_arr = new Func<Func<int, bool>, bool>[] { DelegateTargetForNestedFunc<Func<int, bool>> }; + var m_obj = new Math (); + Func<Func<int, bool>, bool> fn_del_null = null; + Func<int, bool> fs = (i) => i == 0; + fn_func (fs); + fn_del_arr[0](fs); + fn_func_arr[0](fs); + Console.WriteLine ("DelegatesSignatureTest: Just a test message, ignore"); + return 0; + } + + public static void DelegatesAsMethodArgsTest () + { + var _dst_arr = new DelegateForSignatureTest[] { + GenericStruct<int>.DelegateTargetForSignatureTest, + (m, gs) => new GenericStruct<bool[]> () + }; + Func<char[], bool> _fn_func = (cs) => cs.Length == 0; + Action<GenericStruct<int>[]> _fn_action = (gss) => { }; + + new Math ().MethodWithDelegateArgs (_dst_arr, _fn_func, _fn_action); + } + + void MethodWithDelegateArgs (Math.DelegateForSignatureTest[] dst_arr, Func<char[], bool> fn_func, + Action<GenericStruct<int>[]> fn_action) + { + Console.WriteLine ($"Placeholder for breakpoint"); + } + + public static async System.Threading.Tasks.Task MethodWithDelegatesAsyncTest () + { + await new Math ().MethodWithDelegatesAsync (); + } + + async System.Threading.Tasks.Task MethodWithDelegatesAsync () + { + var _dst_arr = new DelegateForSignatureTest[] { + GenericStruct<int>.DelegateTargetForSignatureTest, + (m, gs) => new GenericStruct<bool[]> () + }; + Func<char[], bool> _fn_func = (cs) => cs.Length == 0; + Action<GenericStruct<int>[]> _fn_action = (gss) => { }; + + Console.WriteLine ($"Placeholder for breakpoint"); + await System.Threading.Tasks.Task.CompletedTask; + } + + delegate void DelegateWithVoidReturn (GenericStruct<int[]> gs); + static void DelegateTargetWithVoidReturn (GenericStruct<int[]> gs) { } + + delegate GenericStruct<bool[]> DelegateForSignatureTest (Math m, GenericStruct<GenericStruct<int[]>> gs); + static bool DelegateTargetForNestedFunc<T>(T arg) => true; + struct SimpleStruct { public DateTime dt; @@ -155,6 +279,9 @@ public class Math { //Only append content to this class as the test suite depend { public System.Collections.Generic.List<T> List; public string StringField; + + public static GenericStruct<bool[]> DelegateTargetForSignatureTest (Math m, GenericStruct<GenericStruct<T[]>> gs) + => new GenericStruct<bool[]> (); } } diff --git a/sdks/wasm/src/library_mono.js b/sdks/wasm/src/library_mono.js index 250614115a5..538112d7c1a 100644 --- a/sdks/wasm/src/library_mono.js +++ b/sdks/wasm/src/library_mono.js @@ -437,7 +437,7 @@ var MonoSupportLib = { { // Fix up generic names like Foo`2<int, string> to Foo<int, string> // and nested class names like Foo/Bar to Foo.Bar - return className.replace('/', '.').replace(/`\d+/, ''); + return className.replace(/\//g, '.').replace(/`\d+/g, ''); }, }, @@ -618,20 +618,57 @@ var MonoSupportLib = { }); }, - mono_wasm_add_func_var: function(className, objectId) { + /* + * @className, and @targetName are in the following format: + * + * <ret_type>:[<comma separated list of arg types>]:<method name> + */ + mono_wasm_add_func_var: function (className, targetName, objectId) { if (objectId == 0) { - MONO.mono_wasm_add_null_var (className); + MONO.mono_wasm_add_null_var ( + MONO._mono_csharp_fixup_class_name (Module.UTF8ToString (className))); return; } - fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className)); - MONO.var_info.push({ - value: { - type: "function", - description: fixed_class_name, - objectId: "dotnet:object:"+ objectId, - } - }); + function args_to_sig (args_str) { + var parts = args_str.split (":"); + // TODO: min length = 3? + parts = parts.map (a => MONO._mono_csharp_fixup_class_name (a)); + + // method name at the end + var method_name = parts.pop (); + + // ret type at the beginning + var ret_sig = parts [0]; + var args_sig = parts.splice (1).join (', '); + return `${ret_sig} ${method_name} (${args_sig})`; + } + + var tgt_sig; + if (targetName != 0) + tgt_sig = args_to_sig (Module.UTF8ToString (targetName)); + + var type_name = MONO._mono_csharp_fixup_class_name (Module.UTF8ToString (className)); + + if (objectId == -1) { + // Target property + MONO.var_info.push ({ + value: { + type: "symbol", + value: tgt_sig, + description: tgt_sig, + } + }); + } else { + MONO.var_info.push ({ + value: { + type: "object", + className: type_name, + description: tgt_sig, + objectId: "dotnet:object:" + objectId, + } + }); + } }, mono_wasm_add_frame: function(il, method, name) { |