Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
9 changes: 7 additions & 2 deletions src/ObjCRuntime/Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2704,8 +2704,13 @@ public static string? OriginalWorkingDirectory {
static nint InvokeConformsToProtocol (IntPtr gchandle, IntPtr handle, IntPtr protocol)
{
var obj = GetGCHandleTarget (gchandle) as NSObject;
if (obj is null)
return 0;
if (obj is null) {
// conformsToProtocol is special, we might get here before a managed instance has been created
// for a native instance, in which case we need to create the managed instance (which GetNSObject will do)
obj = GetNSObject (handle);
if (obj is null)
return 0;
}
var rv = obj.ConformsToProtocol (protocol);
return rv ? 1 : 0;
}
Expand Down
4 changes: 4 additions & 0 deletions tests/bindings-test/ApiDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -840,4 +840,8 @@ interface WrappedNSDictionary {
UIEdgeInsets UIEdgeInsetsField { get; set; }
#endif // !__MACOS____MACOS__
}

[BaseType (typeof (NSObject))]
[DisableDefaultCtor]
interface ClassWithNoDefaultCtor { }
}
22 changes: 22 additions & 0 deletions tests/monotouch-test/ObjCRuntime/RuntimeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using UIKit;
#endif

using Bindings.Test;
using MonoTests.System.Net.Http;
using Xamarin.Utils;

Expand Down Expand Up @@ -912,6 +913,27 @@ class IntPtrBoolConstructor : DisposableObject {
{
}
}

[Test]
public void ConformsToProtocolCreatesManagedInstance ()
{
// The point of this test is to verify what happens if an instance of a subclassed type is created
// through an Objective-C initializer that is not bound in managed code. In this case, no managed
// code will be executed when the native instance is created, which means there won't be a managed
// instance either.
// At some point this caused trouble for our conformsToProtocol: override/implementation: we'd just
// return 'false' if there was no managed instance for a native object.
var cls = new Class (typeof (ConformsToProtocolCreatesManagedInstanceTestClass));
var handle = Messaging.IntPtr_objc_msgSend (cls.Handle, Selector.GetHandle ("alloc"));
handle = Messaging.IntPtr_objc_msgSend (handle, Selector.GetHandle ("init"));
var conforms = Messaging.bool_objc_msgSend_IntPtr (handle, Selector.GetHandle ("conformsToProtocol:"), Runtime.GetProtocol ("NSObject"));
Messaging.void_objc_msgSend (handle, Selector.GetHandle ("release"));
Assert.That (conforms, Is.EqualTo (true), "Conforms");
}

class ConformsToProtocolCreatesManagedInstanceTestClass : ClassWithNoDefaultCtor {
protected ConformsToProtocolCreatesManagedInstanceTestClass (NativeHandle handle) : base (handle) {}
}
}

[TestFixture]
Expand Down
4 changes: 4 additions & 0 deletions tests/test-libraries/libtest.h
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ typedef void (^outerBlock) (innerBlock callback);
@property (retain) NSPointerArray* array;
@end

@interface ClassWithNoDefaultCtor : NSObject {
}
@end

#pragma clang diagnostic pop
// NS_ASSUME_NONNULL_END

Expand Down
4 changes: 4 additions & 0 deletions tests/test-libraries/libtest.m
Original file line number Diff line number Diff line change
Expand Up @@ -1535,4 +1535,8 @@ -(void) callDoSomething: (int*) nilObjectCount nonNilObjectCount: (int *) nonNil

@end

@implementation ClassWithNoDefaultCtor : NSObject {
}
@end

#include "libtest.decompile.m"
Loading