[runtime] Call mono_unhandled_exception to raise AppDomain.UnhandledException. (#20656)
Call mono_unhandled_exception to raise AppDomain.UnhandledException when managed exceptions are unhandled. Partial fix for #15252 (for MonoVM, still pending for CoreCLR, which needs https://github.com/dotnet/runtime/issues/102730 fixed first).
This commit is contained in:
Родитель
1c7604cde2
Коммит
a0b858ad47
|
@ -1174,4 +1174,11 @@ mono_string_new (MonoDomain *domain, const char *text)
|
|||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
xamarin_bridge_raise_unhandled_exception_event (GCHandle exception_gchandle)
|
||||
{
|
||||
// There's no way to raise the AppDomain.UnhandledException event.
|
||||
// https://github.com/dotnet/runtime/issues/102730
|
||||
}
|
||||
|
||||
#endif // CORECLR_RUNTIME
|
||||
|
|
|
@ -169,6 +169,12 @@
|
|||
XamarinRuntime = RuntimeMode.MonoVM,
|
||||
},
|
||||
|
||||
new Export ("void", "mono_unhandled_exception",
|
||||
"MonoObject *", "ex"
|
||||
) {
|
||||
XamarinRuntime = RuntimeMode.MonoVM,
|
||||
},
|
||||
|
||||
new Export ("char*", "mono_array_addr_with_size",
|
||||
"MonoArray *", "array",
|
||||
"int", "size",
|
||||
|
|
|
@ -563,4 +563,11 @@ xamarin_enable_new_refcount ()
|
|||
mono_profiler_install_gc (gc_event_callback, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
xamarin_bridge_raise_unhandled_exception_event (GCHandle exception_gchandle)
|
||||
{
|
||||
MonoObject *exc = xamarin_gchandle_get_target (exception_gchandle);
|
||||
mono_unhandled_exception (exc);
|
||||
}
|
||||
|
||||
#endif // !CORECLR_RUNTIME
|
||||
|
|
|
@ -1143,6 +1143,16 @@ exception_handler (NSException *exc)
|
|||
// COOP: We won't get here in coop-mode, because we don't set the uncaught objc exception handler in that case.
|
||||
LOG (PRODUCT ": Received unhandled ObjectiveC exception: %@ %@", [exc name], [exc reason]);
|
||||
|
||||
XamarinGCHandle* exc_handle = [[exc userInfo] objectForKey: @"XamarinManagedExceptionHandle"];
|
||||
if (exc_handle != NULL) {
|
||||
GCHandle exception_gchandle = [exc_handle getHandle];
|
||||
if (exception_gchandle != INVALID_GCHANDLE) {
|
||||
xamarin_bridge_raise_unhandled_exception_event (exception_gchandle);
|
||||
PRINT ("Received unhandled Objective-C exception that was marshalled from a managed exception: %@", exc);
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
|
||||
if (xamarin_handling_unhandled_exceptions == 1) {
|
||||
PRINT ("Detected recursion when handling uncaught Objective-C exception: %@", exc);
|
||||
abort ();
|
||||
|
|
|
@ -223,6 +223,7 @@ void xamarin_bridge_call_runtime_initialize (struct InitializationOptions* opt
|
|||
void xamarin_bridge_register_product_assembly (GCHandle* exception_gchandle);
|
||||
MonoMethod * xamarin_bridge_get_mono_method (MonoReflectionMethod *method);
|
||||
void xamarin_bridge_free_mono_signature (MonoMethodSignature **signature);
|
||||
void xamarin_bridge_raise_unhandled_exception_event (GCHandle exception_gchandle); // the GCHandle is *not* freed. This method will return after raising the event.
|
||||
bool xamarin_register_monoassembly (MonoAssembly *assembly, GCHandle *exception_gchandle);
|
||||
void xamarin_install_nsautoreleasepool_hooks ();
|
||||
void xamarin_enable_new_refcount ();
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Foundation;
|
||||
|
||||
namespace MySimpleApp {
|
||||
public class Program {
|
||||
static int Main (string [] args)
|
||||
{
|
||||
GC.KeepAlive (typeof (NSObject)); // prevent linking away the platform assembly
|
||||
|
||||
var testCaseString = Environment.GetEnvironmentVariable ("EXCEPTIONAL_TEST_CASE");
|
||||
if (string.IsNullOrEmpty (testCaseString)) {
|
||||
Console.WriteLine ($"The environment variable EXCEPTIONAL_TEST_CASE wasn't set.");
|
||||
return 2;
|
||||
}
|
||||
var testCase = int.Parse (testCaseString);
|
||||
switch (testCase) {
|
||||
case 1:
|
||||
AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => {
|
||||
if (e.ExceptionObject is TestCaseException) {
|
||||
Console.WriteLine (Environment.GetEnvironmentVariable ("MAGIC_WORD"));
|
||||
} else {
|
||||
Console.WriteLine ($"Unexpected exception type: {e.ExceptionObject?.GetType ()}");
|
||||
}
|
||||
Environment.Exit (0);
|
||||
};
|
||||
throw new TestCaseException ();
|
||||
default:
|
||||
Console.WriteLine ($"Unknown test case: {testCase}");
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestCaseException : Exception {
|
||||
public TestCaseException ()
|
||||
: base ("Testing, testing")
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-maccatalyst</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\shared.csproj" />
|
||||
</Project>
|
|
@ -0,0 +1 @@
|
|||
include ../shared.mk
|
|
@ -0,0 +1,2 @@
|
|||
TOP=../../..
|
||||
include $(TOP)/tests/common/shared-dotnet-test.mk
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-ios</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\shared.csproj" />
|
||||
</Project>
|
|
@ -0,0 +1 @@
|
|||
include ../shared.mk
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-macos</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\shared.csproj" />
|
||||
</Project>
|
|
@ -0,0 +1 @@
|
|||
include ../shared.mk
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
|
||||
<ApplicationTitle>ExceptionalTestApp</ApplicationTitle>
|
||||
<ApplicationId>com.xamarin.exceptionaltestapp</ApplicationId>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="../../common/shared-dotnet.csproj" />
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="../*.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,3 @@
|
|||
TOP=../../../..
|
||||
TESTNAME=MySimpleApp
|
||||
include $(TOP)/tests/common/shared-dotnet.mk
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-tvos</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\shared.csproj" />
|
||||
</Project>
|
|
@ -0,0 +1 @@
|
|||
include ../shared.mk
|
|
@ -1764,5 +1764,31 @@ namespace Xamarin.Tests {
|
|||
|
||||
Assert.AreEqual ($"sourcelink test passed: {pdbFile}", test.StandardOutput.ToString ().TrimEnd ('\n'));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
// [TestCase (ApplePlatform.iOS)] // Skipping because we're not executing tvOS apps anyway (but it should work)
|
||||
// [TestCase (ApplePlatform.TVOS)] // Skipping because we're not executing tvOS apps anyway (but it should work)
|
||||
[TestCase (ApplePlatform.MacOSX)] // https://github.com/dotnet/runtime/issues/102730
|
||||
[TestCase (ApplePlatform.MacCatalyst)]
|
||||
public void RaisesAppDomainUnhandledExceptionEvent (ApplePlatform platform)
|
||||
{
|
||||
var project = "ExceptionalTestApp";
|
||||
Configuration.IgnoreIfIgnoredPlatform (platform);
|
||||
|
||||
var runtimeIdentifiers = GetDefaultRuntimeIdentifier (platform);
|
||||
var project_path = GetProjectPath (project, runtimeIdentifiers: runtimeIdentifiers, platform: platform, out var appPath);
|
||||
Clean (project_path);
|
||||
var properties = GetDefaultProperties ();
|
||||
DotNet.AssertBuild (project_path, properties);
|
||||
|
||||
if (CanExecute (platform, runtimeIdentifiers)) {
|
||||
var env = new Dictionary<string, string?> {
|
||||
{ "EXCEPTIONAL_TEST_CASE", "1" },
|
||||
};
|
||||
var appExecutable = GetNativeExecutable (platform, appPath);
|
||||
var output = ExecuteWithMagicWordAndAssert (appExecutable, env);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -327,28 +327,28 @@ namespace Xamarin.Tests {
|
|||
return csproj;
|
||||
}
|
||||
|
||||
protected string ExecuteWithMagicWordAndAssert (ApplePlatform platform, string runtimeIdentifiers, string executable)
|
||||
protected string ExecuteWithMagicWordAndAssert (ApplePlatform platform, string runtimeIdentifiers, string executable, Dictionary<string, string?>? environment = null)
|
||||
{
|
||||
if (!CanExecute (platform, runtimeIdentifiers))
|
||||
return string.Empty;
|
||||
|
||||
return ExecuteWithMagicWordAndAssert (executable);
|
||||
return ExecuteWithMagicWordAndAssert (executable, environment);
|
||||
}
|
||||
|
||||
protected string ExecuteWithMagicWordAndAssert (string executable)
|
||||
protected string ExecuteWithMagicWordAndAssert (string executable, Dictionary<string, string?>? environment = null)
|
||||
{
|
||||
if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
|
||||
Console.WriteLine ($"Not executing '{executable}' because we're on Windows.");
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var rv = Execute (executable, out var output, out string magicWord);
|
||||
var rv = Execute (executable, out var output, out string magicWord, environment);
|
||||
Assert.That (output.ToString (), Does.Contain (magicWord), "Contains magic word");
|
||||
Assert.AreEqual (0, rv.ExitCode, "ExitCode");
|
||||
return output.ToString ();
|
||||
}
|
||||
|
||||
protected Execution Execute (string executable, out StringBuilder output, out string magicWord)
|
||||
protected Execution Execute (string executable, out StringBuilder output, out string magicWord, Dictionary<string, string?>? environment = null)
|
||||
{
|
||||
if (!File.Exists (executable))
|
||||
throw new FileNotFoundException ($"The executable '{executable}' does not exists.");
|
||||
|
@ -358,6 +358,10 @@ namespace Xamarin.Tests {
|
|||
{ "MAGIC_WORD", magicWord },
|
||||
{ "DYLD_FALLBACK_LIBRARY_PATH", null }, // VSMac might set this, which may cause tests to crash.
|
||||
};
|
||||
if (environment is not null) {
|
||||
foreach (var kvp in environment)
|
||||
env [kvp.Key] = kvp.Value;
|
||||
}
|
||||
|
||||
output = new StringBuilder ();
|
||||
return Execution.RunWithStringBuildersAsync (executable, Array.Empty<string> (), environment: env, standardOutput: output, standardError: output, timeout: TimeSpan.FromSeconds (15)).Result;
|
||||
|
|
Загрузка…
Ссылка в новой задаче