Skip to content

Commit 72604a4

Browse files
authored
[browser][debugger] fake bind_static_method (#96899)
1 parent 300013b commit 72604a4

File tree

4 files changed

+285
-8
lines changed

4 files changed

+285
-8
lines changed

src/mono/browser/debugger/DebuggerTestSuite/ExceptionTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public async Task ExceptionTestAll()
2525

2626
await SetPauseOnException("all");
2727

28-
var eval_expr = "window.setTimeout(function() { invoke_static_method (" +
28+
var eval_expr = "window.setTimeout(function() { invoke_static_method_native (" +
2929
$"'{entry_method_name}'" +
3030
"); }, 1);";
3131

@@ -157,7 +157,7 @@ await CheckValue(eo["exceptionDetails"]?["exception"], JObject.FromObject(new
157157
{
158158
type = "object",
159159
subtype = "error",
160-
className = "Error" // BUG?: "DebuggerTests.CustomException"
160+
className = "ManagedError" // BUG?: "DebuggerTests.CustomException"
161161
}), "exception");
162162

163163
return;
@@ -201,7 +201,7 @@ await CheckValue(eo["exceptionDetails"]?["exception"], JObject.FromObject(new
201201

202202
[ConditionalTheory(nameof(RunningOnChrome))]
203203
[InlineData("function () { exceptions_test (); }", null, 0, 0, "exception_uncaught_test", "RangeError", "exception uncaught")]
204-
[InlineData("function () { invoke_static_method ('[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions'); }",
204+
[InlineData("function () { invoke_static_method_native ('[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions'); }",
205205
"dotnet://debugger-test.dll/debugger-exception-test.cs", 28, 16, "DebuggerTests.ExceptionTestsClass.TestUncaughtException.run",
206206
"DebuggerTests.CustomException", "not implemented uncaught")]
207207
public async Task ExceptionTestUncaught(string eval_fn, string loc, int line, int col, string fn_name,
@@ -240,7 +240,7 @@ await SendCommand("Page.reload", JObject.FromObject(new
240240
}));
241241
await insp.WaitFor(Inspector.APP_READY);
242242

243-
var eval_expr = "window.setTimeout(function() { invoke_static_method (" +
243+
var eval_expr = "window.setTimeout(function() { invoke_static_method_native (" +
244244
$"'{entry_method_name}'" +
245245
"); }, 1);";
246246

@@ -303,7 +303,7 @@ await insp.WaitFor(Inspector.PAUSE)
303303
await taskWait;
304304
_testOutput.WriteLine ($"* Resumed {count} times");
305305

306-
var eval_expr = "window.setTimeout(function() { invoke_static_method (" +
306+
var eval_expr = "window.setTimeout(function() { invoke_static_method_native (" +
307307
$"'{entry_method_name}'" +
308308
"); }, 1);";
309309

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Runtime.InteropServices.JavaScript;
5+
using System;
6+
using System.Reflection;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
using System.Threading;
10+
using System.IO;
11+
using System.Runtime.CompilerServices;
12+
using System.Runtime.InteropServices;
13+
using System.Collections.Generic;
14+
15+
namespace DebuggerTests
16+
{
17+
// this is fake implementation of legacy `bind_static_method`
18+
// so that we don't have to rewrite all the tests which use it via `invoke_static_method`
19+
public sealed partial class BindStaticMethod
20+
{
21+
22+
[JSExport]
23+
[return: JSMarshalAs<JSType.Any>()]
24+
public static object GetMethodInfo(string monoMethodName)
25+
{
26+
return GetMethodInfoImpl(monoMethodName);
27+
}
28+
29+
[JSExport]
30+
public static unsafe IntPtr GetMonoMethodPtr(string monoMethodName)
31+
{
32+
var methodInfo = GetMethodInfoImpl(monoMethodName);
33+
var temp = new IntPtrAndHandle { methodHandle = methodInfo.MethodHandle };
34+
return temp.ptr;
35+
}
36+
37+
public static MethodInfo GetMethodInfoImpl(string monoMethodName)
38+
{
39+
ArgumentNullException.ThrowIfNullOrEmpty(monoMethodName, nameof(monoMethodName));
40+
// [debugger-test] DebuggerTests.ArrayTestsClass:ObjectArrayMembers
41+
var partsA = monoMethodName.Split(' ');
42+
var assemblyName = partsA[0].Substring(1, partsA[0].Length - 2);
43+
var partsN = partsA[1].Split(':');
44+
var className = partsN[0];
45+
var methodName = partsN[1];
46+
47+
var typeName = $"{className}, {assemblyName}";
48+
Type type = Type.GetType(typeName);
49+
if (type == null)
50+
{
51+
throw new ArgumentException($"Type not found {typeName}");
52+
}
53+
54+
var method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
55+
if (method == null)
56+
{
57+
throw new ArgumentException($"Method not found {className}.{methodName}");
58+
}
59+
60+
return method;
61+
}
62+
63+
[JSExport]
64+
public static string GetSignature([JSMarshalAs<JSType.Any>()] object methodInfo)
65+
{
66+
var method = (MethodInfo)methodInfo;
67+
var sb = new StringBuilder("Invoke");
68+
foreach (var p in method.GetParameters())
69+
{
70+
sb.Append("_");
71+
if (typeof(Task).IsAssignableFrom(p.ParameterType))
72+
{
73+
sb.Append("Task");
74+
}
75+
else if (p.ParameterType.GenericTypeArguments.Length > 0)
76+
{
77+
throw new NotImplementedException($"Parameter {p.Name} type {p.ParameterType.FullName}");
78+
}
79+
else
80+
{
81+
sb.Append(p.ParameterType.Name);
82+
}
83+
}
84+
85+
sb.Append("_");
86+
if (typeof(Task).IsAssignableFrom(method.ReturnType))
87+
{
88+
sb.Append("Task");
89+
}
90+
else if (method.ReturnType.GenericTypeArguments.Length > 0)
91+
{
92+
throw new NotImplementedException($"Method return type {method.ReturnType.FullName}");
93+
}
94+
else
95+
{
96+
sb.Append(method.ReturnType.Name);
97+
}
98+
99+
return sb.ToString();
100+
}
101+
102+
[JSExport]
103+
public static void Invoke_Void([JSMarshalAs<JSType.Any>()] object methodInfo)
104+
{
105+
var method = (MethodInfo)methodInfo;
106+
method.Invoke(null, null);
107+
}
108+
109+
[JSExport]
110+
public static Task Invoke_Task([JSMarshalAs<JSType.Any>()] object methodInfo)
111+
{
112+
var method = (MethodInfo)methodInfo;
113+
return (Task)method.Invoke(null, null);
114+
}
115+
116+
[JSExport]
117+
public static string Invoke_String([JSMarshalAs<JSType.Any>()] object methodInfo)
118+
{
119+
var method = (MethodInfo)methodInfo;
120+
return (string)method.Invoke(null, null);
121+
}
122+
123+
[JSExport]
124+
public static void Invoke_Boolean_Void([JSMarshalAs<JSType.Any>()] object methodInfo, bool p1)
125+
{
126+
var method = (MethodInfo)methodInfo;
127+
method.Invoke(null, new object[] { p1 });
128+
}
129+
130+
[JSExport]
131+
public static Task Invoke_Boolean_Task([JSMarshalAs<JSType.Any>()] object methodInfo, bool p1)
132+
{
133+
var method = (MethodInfo)methodInfo;
134+
return (Task)method.Invoke(null, new object[] { p1 });
135+
}
136+
137+
[JSExport]
138+
public static void Invoke_Int32_Void([JSMarshalAs<JSType.Any>()] object methodInfo, int p1)
139+
{
140+
var method = (MethodInfo)methodInfo;
141+
method.Invoke(null, new object[] { p1 });
142+
}
143+
144+
[JSExport]
145+
public static void Invoke_Int32_Int32_Void([JSMarshalAs<JSType.Any>()] object methodInfo, int p1, int p2)
146+
{
147+
var method = (MethodInfo)methodInfo;
148+
method.Invoke(null, new object[] { p1, p2 });
149+
}
150+
151+
[JSExport]
152+
public static void Invoke_Int32_Int32_Int32_Void([JSMarshalAs<JSType.Any>()] object methodInfo, int p1, int p2, int p3)
153+
{
154+
var method = (MethodInfo)methodInfo;
155+
method.Invoke(null, new object[] { p1, p2, p3 });
156+
}
157+
158+
[JSExport]
159+
public static int Invoke_Int32([JSMarshalAs<JSType.Any>()] object methodInfo)
160+
{
161+
var method = (MethodInfo)methodInfo;
162+
return (int)method.Invoke(null, null);
163+
}
164+
165+
[JSExport]
166+
public static int Invoke_Int32_Int32([JSMarshalAs<JSType.Any>()] object methodInfo, int p1)
167+
{
168+
var method = (MethodInfo)methodInfo;
169+
return (int)method.Invoke(null, new object[] { p1 });
170+
}
171+
172+
[JSExport]
173+
public static int Invoke_Int32_Int32_Int32([JSMarshalAs<JSType.Any>()] object methodInfo, int p1, int p2)
174+
{
175+
var method = (MethodInfo)methodInfo;
176+
return (int)method.Invoke(null, new object[] { p1, p2 });
177+
}
178+
179+
[JSExport]
180+
public static void Invoke_String_Void([JSMarshalAs<JSType.Any>()] object methodInfo, string p1)
181+
{
182+
var method = (MethodInfo)methodInfo;
183+
method.Invoke(null, new object[] { p1 });
184+
}
185+
186+
[JSExport]
187+
public static void Invoke_String_String_Void([JSMarshalAs<JSType.Any>()] object methodInfo, string p1, string p2)
188+
{
189+
var method = (MethodInfo)methodInfo;
190+
method.Invoke(null, new object[] { p1, p2 });
191+
}
192+
193+
[JSExport]
194+
public static void Invoke_String_String_String_String_Void([JSMarshalAs<JSType.Any>()] object methodInfo, string p1, string p2, string p3, string p4)
195+
{
196+
var method = (MethodInfo)methodInfo;
197+
method.Invoke(null, new object[] { p1, p2, p3, p4 });
198+
}
199+
200+
[JSExport]
201+
public static string Invoke_String_String_String([JSMarshalAs<JSType.Any>()] object methodInfo, string p1, string p2)
202+
{
203+
var method = (MethodInfo)methodInfo;
204+
return (string)method.Invoke(null, new object[] { p1, p2 });
205+
}
206+
207+
[JSExport]
208+
public static void Invoke_String_String_String_String_String_String_String_String_Void([JSMarshalAs<JSType.Any>()] object methodInfo, string p1, string p2, string p3, string p4, string p5, string p6, string p7, string p8)
209+
{
210+
var method = (MethodInfo)methodInfo;
211+
method.Invoke(null, new object[] { p1, p2, p3, p4, p5, p6, p7, p8 });
212+
}
213+
214+
[StructLayout(LayoutKind.Explicit)]
215+
private struct IntPtrAndHandle
216+
{
217+
[FieldOffset(0)]
218+
internal IntPtr ptr;
219+
220+
[FieldOffset(0)]
221+
internal RuntimeMethodHandle methodHandle;
222+
223+
[FieldOffset(0)]
224+
internal RuntimeTypeHandle typeHandle;
225+
}
226+
}
227+
}

src/mono/browser/debugger/tests/debugger-test/debugger-driver.html

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
var App = {
88
static_method_table: {},
99
init: async () => {
10-
const exports = await App.runtime.getAssemblyExports("debugger-test.dll");
10+
const exports = App.exports = await App.runtime.getAssemblyExports("debugger-test.dll");
1111
App.int_add = exports.Math.IntAdd;
1212
App.use_complex = exports.Math.UseComplex;
1313
App.delegates_test = exports.Math.DelegatesTest;
@@ -23,15 +23,15 @@
2323
function invoke_static_method (method_name, ...args) {
2424
var method = App.static_method_table [method_name];
2525
if (method == undefined)
26-
method = App.static_method_table[method_name] = App.runtime.BINDING.bind_static_method(method_name);
26+
method = App.static_method_table[method_name] = App.bind_static_method(method_name);
2727

2828
return method (...args);
2929
}
3030

3131
async function invoke_static_method_async (method_name, ...args) {
3232
var method = App.static_method_table [method_name];
3333
if (method == undefined) {
34-
method = App.static_method_table[method_name] = App.runtime.BINDING.bind_static_method(method_name);
34+
method = App.static_method_table[method_name] = App.bind_static_method(method_name);
3535
}
3636

3737
return await method (...args);
@@ -92,6 +92,14 @@
9292
window.location.replace("http://localhost:9400/wasm-page-without-assets.html");
9393
console.debug ("#debugger-app-ready#");
9494
}
95+
function invoke_static_method_native (method_name, ...args) {
96+
const native_method_name = "Native:" + method_name;
97+
var method = App.static_method_table [native_method_name];
98+
if (method == undefined)
99+
method = App.static_method_table[native_method_name] = App.bind_static_method_native(method_name);
100+
101+
return method (...args);
102+
}
95103
</script>
96104

97105
<script type="text/javascript" src="other.js"></script>

src/mono/browser/debugger/tests/debugger-test/debugger-main.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,48 @@ try {
1919
debugger: (level, message) => console.log({ level, message }),
2020
};*/
2121
App.runtime = runtime;
22+
23+
// this is fake implementation of legacy `bind_static_method`
24+
// so that we don't have to rewrite all the tests which use it via `invoke_static_method`
25+
App.bind_static_method = (method_name) => {
26+
const methodInfo = App.exports.DebuggerTests.BindStaticMethod.GetMethodInfo(method_name);
27+
const signature = App.exports.DebuggerTests.BindStaticMethod.GetSignature(methodInfo);
28+
const invoker = App.exports.DebuggerTests.BindStaticMethod[signature];
29+
if (!invoker) {
30+
const message = `bind_static_method: Could not find invoker for ${method_name} with signature ${signature}`;
31+
console.error(message);
32+
throw new Error(message);
33+
}
34+
return function () {
35+
return invoker(methodInfo, ...arguments);
36+
}
37+
}
38+
39+
// this is fake implementation of legacy `bind_static_method` which uses `mono_wasm_invoke_method_raw`
40+
// We have unit tests that stop on unhandled managed exceptions.
41+
// as opposed to [JSExport], the `mono_wasm_invoke_method_raw` doesn't handle managed exceptions.
42+
// Same way as old `bind_static_method` didn't
43+
App.bind_static_method_native = (method_name) => {
44+
try {
45+
const monoMethodPtr = App.exports.DebuggerTests.BindStaticMethod.GetMonoMethodPtr(method_name);
46+
// this is only implemented for void methods with no arguments
47+
const invoker = runtime.Module.cwrap("mono_wasm_invoke_method_raw", "number", ["number", "number"]);
48+
return function () {
49+
try {
50+
return invoker(monoMethodPtr);
51+
}
52+
catch (err) {
53+
console.error(err);
54+
throw err;
55+
}
56+
}
57+
}
58+
catch (err) {
59+
console.error(err);
60+
throw err;
61+
}
62+
}
63+
2264
await App.init();
2365
}
2466
catch (err) {

0 commit comments

Comments
 (0)