[runtime] Throw a managed exception instead of trying to call a null function pointer if the runtime tries to call a function that has been linked away. (#4770)
* [runtime] Throw a managed exception instead of trying to call a null function pointer if the runtime tries to call a function that has been linked away. * [tests] Add new file for Xamarin.Mac tests. * Direct people to file issues in github.
This commit is contained in:
Родитель
72c3b33284
Коммит
385592b128
|
@ -522,3 +522,8 @@ There are a few reasons this may happen:
|
|||
|
||||
* It could be a bug in Xamarin.Mac. If this is the case, please file a bug at
|
||||
[https://bugzilla.xamarin.com](https://bugzilla.xamarin.com/enter_bug.cgi?product=Xamarin.Mac).
|
||||
|
||||
### <a name="MM8028"/>MM8028: The runtime function {function} has been linked away.
|
||||
|
||||
This usually indicates a bug in Xamarin.Mac, because runtime functions should
|
||||
not be linked away if they're needed. Please [submit an issue](https://github.com/xamarin/xamarin-macios/wiki/Submitting-Bugs-&-Suggestions).
|
||||
|
|
|
@ -2541,3 +2541,8 @@ There are a few reasons this may happen:
|
|||
|
||||
* It could be a bug in Xamarin.iOS. If this is the case, please file a bug at
|
||||
[https://bugzilla.xamarin.com](https://bugzilla.xamarin.com/enter_bug.cgi?product=iOS).
|
||||
|
||||
### <a name="MT8028"/>MT8028: The runtime function {function} has been linked away.
|
||||
|
||||
This usually indicates a bug in Xamarin.iOS, because runtime functions should
|
||||
not be linked away if they're needed. Please [submit an issue](https://github.com/xamarin/xamarin-macios/wiki/Submitting-Bugs-&-Suggestions).
|
||||
|
|
|
@ -27,10 +27,24 @@ struct Delegates {
|
|||
|
||||
static struct Delegates delegates = { 0 };
|
||||
|
||||
static guint32
|
||||
create_linked_away_exception (const char *func)
|
||||
{
|
||||
char *msg = xamarin_strdup_printf ("The runtime function %s has been linked away.", func);
|
||||
guint32 gchandle = xamarin_create_product_exception (8028, msg);
|
||||
xamarin_free (msg);
|
||||
return gchandle;
|
||||
}
|
||||
|
||||
<# foreach (var d in delegates) { #>
|
||||
<#= d.CReturnType #>
|
||||
<#= d.EntryPoint #> (<#= d.CArgumentSignature #>)
|
||||
{
|
||||
<#if (d.ExceptionHandling && d.OnlyDynamicUsage) {#>if (delegates.<#= d.EntryPoint.Substring ("xamarin_".Length) #> == NULL) {
|
||||
*exception_gchandle = create_linked_away_exception ("<#= d.EntryPoint.Substring ("xamarin_".Length) #>");
|
||||
return<# if (d.CReturnType != "void") { #> (<#= d.CReturnType #>) 0<# } #>;
|
||||
}
|
||||
<#}#>
|
||||
<# if (d.CReturnType != "void") { #>return <# } #>delegates.<#= d.EntryPoint.Substring ("xamarin_".Length) #> (<#= d.CArgumentNames #>);
|
||||
}
|
||||
|
||||
|
|
|
@ -2424,14 +2424,18 @@ xamarin_process_managed_exception (MonoObject *exception)
|
|||
|
||||
void
|
||||
xamarin_throw_product_exception (int code, const char *message)
|
||||
{
|
||||
xamarin_process_managed_exception_gchandle (xamarin_create_product_exception (code, message));
|
||||
}
|
||||
|
||||
guint32
|
||||
xamarin_create_product_exception (int code, const char *message)
|
||||
{
|
||||
guint32 exception_gchandle = 0;
|
||||
guint32 handle = xamarin_create_product_exception_for_error (code, message, &exception_gchandle);
|
||||
if (exception_gchandle != 0) {
|
||||
xamarin_process_managed_exception_gchandle (exception_gchandle);
|
||||
} else {
|
||||
xamarin_process_managed_exception_gchandle (handle);
|
||||
}
|
||||
if (exception_gchandle != 0)
|
||||
return exception_gchandle;
|
||||
return handle;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -233,6 +233,7 @@ void xamarin_process_nsexception_using_mode (NSException *ns_exception, bool t
|
|||
void xamarin_process_managed_exception (MonoObject *exc);
|
||||
void xamarin_process_managed_exception_gchandle (guint32 gchandle);
|
||||
void xamarin_throw_product_exception (int code, const char *message);
|
||||
guint32 xamarin_create_product_exception (int code, const char *message);
|
||||
NSString * xamarin_print_all_exceptions (MonoObject *exc);
|
||||
|
||||
id xamarin_invoke_objc_method_implementation (id self, SEL sel, IMP xamarin_impl);
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ObjCRuntime {
|
||||
public static class Messaging {
|
||||
internal const string LIBOBJC_DYLIB = "/usr/lib/libobjc.dylib";
|
||||
|
||||
public struct objc_super {
|
||||
public IntPtr Handle;
|
||||
public IntPtr SuperHandle;
|
||||
}
|
||||
|
||||
[DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
|
||||
public extern static void void_objc_msgSend_IntPtr_bool_bool (IntPtr receiver, IntPtr selector, IntPtr a, bool b, bool c);
|
||||
}
|
||||
}
|
|
@ -245,5 +245,49 @@ namespace Xamarin.BindingTests
|
|||
ObjCBlockTester.CallProtocolWithBlockReturnValue (pb, required, instance);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase (true, true)]
|
||||
[TestCase (true, false)]
|
||||
[TestCase (false, true)]
|
||||
[TestCase (false, false)]
|
||||
public void LinkedAway (bool required, bool instance)
|
||||
{
|
||||
if (!(TestRuntime.IsLinkAll && TestRuntime.IsOptimizeAll))
|
||||
Assert.Ignore ("This test is only applicable if optimized & linking all assemblies.");
|
||||
|
||||
using (var pb = new FakePropertyBlock ()) {
|
||||
try {
|
||||
Messaging.void_objc_msgSend_IntPtr_bool_bool (Class.GetHandle (typeof (ObjCBlockTester)), Selector.GetHandle ("setProtocolWithBlockProperties:required:instance:"), pb.Handle, required, instance);
|
||||
Assert.Fail ("Expected an MT8028 error");
|
||||
} catch (RuntimeException re) {
|
||||
Assert.AreEqual (8028, re.Code, "Code");
|
||||
Assert.AreEqual ("The runtime function get_block_wrapper_creator has been linked away.", re.Message, "Message");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Each method here will show a warning like this:
|
||||
// MT4174: Unable to locate the block to delegate conversion method for the method ...
|
||||
// This is expected.
|
||||
// This is 'fake' because it doesn't implement the ProtocolWithBlockProperties protocol,
|
||||
// which means that XI won't be able to find the delegate<->block conversion methods
|
||||
// (either at runtime or build time).
|
||||
// The point of this test is to ensure that we don't crash when the runtime tries
|
||||
// to find those conversion methods, and instead finds that many required functions
|
||||
// have been linked away (which happen when _forcing_ the dynamic registrar to be linked away).
|
||||
public class FakePropertyBlock : NSObject {
|
||||
[Export ("myOptionalProperty")]
|
||||
public SimpleCallback MyOptionalProperty { get; set; }
|
||||
|
||||
[Export ("myRequiredProperty")]
|
||||
public SimpleCallback MyRequiredProperty { get; set; }
|
||||
|
||||
[Export ("myOptionalStaticProperty")]
|
||||
public static SimpleCallback MyOptionalStaticProperty { get; set; }
|
||||
|
||||
[Export ("myRequiredStaticProperty")]
|
||||
public static SimpleCallback MyRequiredStaticProperty { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@
|
|||
</Compile>
|
||||
<Compile Include="RuntimeTest.cs" />
|
||||
<Compile Include="CodeBehind.cs" />
|
||||
<Compile Include="Messaging.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\tests\test-libraries\libtest.m">
|
||||
|
|
|
@ -80,6 +80,7 @@
|
|||
</Compile>
|
||||
<Compile Include="RuntimeTest.cs" />
|
||||
<Compile Include="CodeBehind.cs" />
|
||||
<Compile Include="Messaging.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\tests\test-libraries\libtest.m">
|
||||
|
|
|
@ -655,4 +655,14 @@ partial class TestRuntime
|
|||
}
|
||||
}
|
||||
class LinkerSentinel { }
|
||||
|
||||
public static bool IsOptimizeAll {
|
||||
get {
|
||||
#if OPTIMIZEALL
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче