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-14 05:09:56 +0300
committerGitHub <noreply@github.com>2020-04-14 05:09:56 +0300
commit4eca12e794764da1a0646e77af5b8c60a2e2767a (patch)
treeb5589f91371a3dbfe6c06b78edede4bad94e3ffe
parent02f1f54f2dc614557c918fcedba47774db5d7b06 (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.c80
-rw-r--r--sdks/wasm/DebuggerTestSuite/Tests.cs606
-rw-r--r--sdks/wasm/debugger-test.cs127
-rw-r--r--sdks/wasm/src/library_mono.js59
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) {