Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/mono/browser/debugger/DebuggerTestSuite/ExceptionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public async Task ExceptionTestAll()

await SetPauseOnException("all");

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

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

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

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

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

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

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

Expand Down
227 changes: 227 additions & 0 deletions src/mono/browser/debugger/tests/debugger-test/BindStaticMethod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices.JavaScript;
using System;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Collections.Generic;

namespace DebuggerTests
{
// this is fake implementation of legacy `bind_static_method`
// so that we don't have to rewrite all the tests which use it via `invoke_static_method`
public sealed partial class BindStaticMethod
{

[JSExport]
[return: JSMarshalAs<JSType.Any>()]
public static object GetMethodInfo(string monoMethodName)
{
return GetMethodInfoImpl(monoMethodName);
}

[JSExport]
public static unsafe IntPtr GetMonoMethodPtr(string monoMethodName)
{
var methodInfo = GetMethodInfoImpl(monoMethodName);
var temp = new IntPtrAndHandle { methodHandle = methodInfo.MethodHandle };
return temp.ptr;
}

public static MethodInfo GetMethodInfoImpl(string monoMethodName)
{
ArgumentNullException.ThrowIfNullOrEmpty(monoMethodName, nameof(monoMethodName));
// [debugger-test] DebuggerTests.ArrayTestsClass:ObjectArrayMembers
var partsA = monoMethodName.Split(' ');
var assemblyName = partsA[0].Substring(1, partsA[0].Length - 2);
var partsN = partsA[1].Split(':');
var className = partsN[0];
var methodName = partsN[1];

var typeName = $"{className}, {assemblyName}";
Type type = Type.GetType(typeName);
if (type == null)
{
throw new ArgumentException($"Type not found {typeName}");
}

var method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (method == null)
{
throw new ArgumentException($"Method not found {className}.{methodName}");
}

return method;
}

[JSExport]
public static string GetSignature([JSMarshalAs<JSType.Any>()] object methodInfo)
{
var method = (MethodInfo)methodInfo;
var sb = new StringBuilder("Invoke");
foreach (var p in method.GetParameters())
{
sb.Append("_");
if (typeof(Task).IsAssignableFrom(p.ParameterType))
{
sb.Append("Task");
}
else if (p.ParameterType.GenericTypeArguments.Length > 0)
{
throw new NotImplementedException($"Parameter {p.Name} type {p.ParameterType.FullName}");
}
else
{
sb.Append(p.ParameterType.Name);
}
}

sb.Append("_");
if (typeof(Task).IsAssignableFrom(method.ReturnType))
{
sb.Append("Task");
}
else if (method.ReturnType.GenericTypeArguments.Length > 0)
{
throw new NotImplementedException($"Method return type {method.ReturnType.FullName}");
}
else
{
sb.Append(method.ReturnType.Name);
}

return sb.ToString();
}

[JSExport]
public static void Invoke_Void([JSMarshalAs<JSType.Any>()] object methodInfo)
{
var method = (MethodInfo)methodInfo;
method.Invoke(null, null);
}

[JSExport]
public static Task Invoke_Task([JSMarshalAs<JSType.Any>()] object methodInfo)
{
var method = (MethodInfo)methodInfo;
return (Task)method.Invoke(null, null);
}

[JSExport]
public static string Invoke_String([JSMarshalAs<JSType.Any>()] object methodInfo)
{
var method = (MethodInfo)methodInfo;
return (string)method.Invoke(null, null);
}

[JSExport]
public static void Invoke_Boolean_Void([JSMarshalAs<JSType.Any>()] object methodInfo, bool p1)
{
var method = (MethodInfo)methodInfo;
method.Invoke(null, new object[] { p1 });
}

[JSExport]
public static Task Invoke_Boolean_Task([JSMarshalAs<JSType.Any>()] object methodInfo, bool p1)
{
var method = (MethodInfo)methodInfo;
return (Task)method.Invoke(null, new object[] { p1 });
}

[JSExport]
public static void Invoke_Int32_Void([JSMarshalAs<JSType.Any>()] object methodInfo, int p1)
{
var method = (MethodInfo)methodInfo;
method.Invoke(null, new object[] { p1 });
}

[JSExport]
public static void Invoke_Int32_Int32_Void([JSMarshalAs<JSType.Any>()] object methodInfo, int p1, int p2)
{
var method = (MethodInfo)methodInfo;
method.Invoke(null, new object[] { p1, p2 });
}

[JSExport]
public static void Invoke_Int32_Int32_Int32_Void([JSMarshalAs<JSType.Any>()] object methodInfo, int p1, int p2, int p3)
{
var method = (MethodInfo)methodInfo;
method.Invoke(null, new object[] { p1, p2, p3 });
}

[JSExport]
public static int Invoke_Int32([JSMarshalAs<JSType.Any>()] object methodInfo)
{
var method = (MethodInfo)methodInfo;
return (int)method.Invoke(null, null);
}

[JSExport]
public static int Invoke_Int32_Int32([JSMarshalAs<JSType.Any>()] object methodInfo, int p1)
{
var method = (MethodInfo)methodInfo;
return (int)method.Invoke(null, new object[] { p1 });
}

[JSExport]
public static int Invoke_Int32_Int32_Int32([JSMarshalAs<JSType.Any>()] object methodInfo, int p1, int p2)
{
var method = (MethodInfo)methodInfo;
return (int)method.Invoke(null, new object[] { p1, p2 });
}

[JSExport]
public static void Invoke_String_Void([JSMarshalAs<JSType.Any>()] object methodInfo, string p1)
{
var method = (MethodInfo)methodInfo;
method.Invoke(null, new object[] { p1 });
}

[JSExport]
public static void Invoke_String_String_Void([JSMarshalAs<JSType.Any>()] object methodInfo, string p1, string p2)
{
var method = (MethodInfo)methodInfo;
method.Invoke(null, new object[] { p1, p2 });
}

[JSExport]
public static void Invoke_String_String_String_String_Void([JSMarshalAs<JSType.Any>()] object methodInfo, string p1, string p2, string p3, string p4)
{
var method = (MethodInfo)methodInfo;
method.Invoke(null, new object[] { p1, p2, p3, p4 });
}

[JSExport]
public static string Invoke_String_String_String([JSMarshalAs<JSType.Any>()] object methodInfo, string p1, string p2)
{
var method = (MethodInfo)methodInfo;
return (string)method.Invoke(null, new object[] { p1, p2 });
}

[JSExport]
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)
{
var method = (MethodInfo)methodInfo;
method.Invoke(null, new object[] { p1, p2, p3, p4, p5, p6, p7, p8 });
}

[StructLayout(LayoutKind.Explicit)]
private struct IntPtrAndHandle
{
[FieldOffset(0)]
internal IntPtr ptr;

[FieldOffset(0)]
internal RuntimeMethodHandle methodHandle;

[FieldOffset(0)]
internal RuntimeTypeHandle typeHandle;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
var App = {
static_method_table: {},
init: async () => {
const exports = await App.runtime.getAssemblyExports("debugger-test.dll");
const exports = App.exports = await App.runtime.getAssemblyExports("debugger-test.dll");
App.int_add = exports.Math.IntAdd;
App.use_complex = exports.Math.UseComplex;
App.delegates_test = exports.Math.DelegatesTest;
Expand All @@ -23,15 +23,15 @@
function invoke_static_method (method_name, ...args) {
var method = App.static_method_table [method_name];
if (method == undefined)
method = App.static_method_table[method_name] = App.runtime.BINDING.bind_static_method(method_name);
method = App.static_method_table[method_name] = App.bind_static_method(method_name);

return method (...args);
}

async function invoke_static_method_async (method_name, ...args) {
var method = App.static_method_table [method_name];
if (method == undefined) {
method = App.static_method_table[method_name] = App.runtime.BINDING.bind_static_method(method_name);
method = App.static_method_table[method_name] = App.bind_static_method(method_name);
}

return await method (...args);
Expand Down Expand Up @@ -92,6 +92,14 @@
window.location.replace("http://localhost:9400/wasm-page-without-assets.html");
console.debug ("#debugger-app-ready#");
}
function invoke_static_method_native (method_name, ...args) {
const native_method_name = "Native:" + method_name;
var method = App.static_method_table [native_method_name];
if (method == undefined)
method = App.static_method_table[native_method_name] = App.bind_static_method_native(method_name);

return method (...args);
}
</script>

<script type="text/javascript" src="other.js"></script>
Expand Down
42 changes: 42 additions & 0 deletions src/mono/browser/debugger/tests/debugger-test/debugger-main.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,48 @@ try {
debugger: (level, message) => console.log({ level, message }),
};*/
App.runtime = runtime;

// this is fake implementation of legacy `bind_static_method`
// so that we don't have to rewrite all the tests which use it via `invoke_static_method`
App.bind_static_method = (method_name) => {
const methodInfo = App.exports.DebuggerTests.BindStaticMethod.GetMethodInfo(method_name);
const signature = App.exports.DebuggerTests.BindStaticMethod.GetSignature(methodInfo);
const invoker = App.exports.DebuggerTests.BindStaticMethod[signature];
if (!invoker) {
const message = `bind_static_method: Could not find invoker for ${method_name} with signature ${signature}`;
console.error(message);
throw new Error(message);
}
return function () {
return invoker(methodInfo, ...arguments);
}
}

// this is fake implementation of legacy `bind_static_method` which uses `mono_wasm_invoke_method_raw`
// We have unit tests that stop on unhandled managed exceptions.
// as opposed to [JSExport], the `mono_wasm_invoke_method_raw` doesn't handle managed exceptions.
// Same way as old `bind_static_method` didn't
App.bind_static_method_native = (method_name) => {
try {
const monoMethodPtr = App.exports.DebuggerTests.BindStaticMethod.GetMonoMethodPtr(method_name);
// this is only implemented for void methods with no arguments
const invoker = runtime.Module.cwrap("mono_wasm_invoke_method_raw", "number", ["number", "number"]);
return function () {
try {
return invoker(monoMethodPtr);
}
catch (err) {
console.error(err);
throw err;
}
}
}
catch (err) {
console.error(err);
throw err;
}
}

await App.init();
}
catch (err) {
Expand Down