Skip to content

Commit

Permalink
[registrar] Create instances of NSObjects and INativeObjects without …
Browse files Browse the repository at this point in the history
…using reflection in the managed static registrar (#18519)

This PR adds lookup tables and factory methods for INativeObjects and
NSObjects. These generated methods allow us to create instances of these
objects without needing reflection.

Closes #18358.
  • Loading branch information
simonrozsival authored Aug 15, 2023
1 parent 8afd241 commit 7f67aa0
Show file tree
Hide file tree
Showing 12 changed files with 602 additions and 41 deletions.
25 changes: 25 additions & 0 deletions docs/website/mtouch-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -3749,3 +3749,28 @@ This exception will have an inner exception which gives the reason for the failu
### MT8036: Failed to convert the value at index {index} from {type} to {type}.

This exception will have an inner exception which gives the reason for the failure.

<a name="MX8056" />

### MX8056: Failed to marshal the Objective-C object {handle} (type: {objc_type}). Could not find an existing managed instance for this object, nor was it possible to create a new managed instance of generic type {managed_type}.

This occurs when the Xamarin.iOS runtime finds an Objective-C object without a
corresponding managed wrapper object, and when trying to create that managed
wrapper, it turns out it's not possible. This error is specific to the managed
static registrar.

There are a few reasons this may happen:

* A managed wrapper existed at some point, but was collected by the GC. If the
native object is still alive, and later resurfaces to managed code, the
Xamarin.iOS runtime will try to re-create a managed wrapper instance. In
most cases the problem here is that the managed wrapper shouldn't have been
collected by the GC in the first place.

Possible causes include:

* Manually calling Dispose too early on the managed wrapper.
* Incorrect bindings for third-party libraries.
* Reference-counting bugs in third-party libraries.

* This indicates a bug in Xamarin.iOS. Please file a new issue on [github](https://github.com/xamarin/xamarin-macios/issues/new).
13 changes: 13 additions & 0 deletions src/Foundation/NSObject2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ public class NSObjectFlag {
}
#endif

#if NET
// This interface will be made public when the managed static registrar is used.
internal interface INSObjectFactory {
// The method will be implemented via custom linker step if the managed static registrar is used
// for NSObject subclasses which have an (NativeHandle) or (IntPtr) constructor.
[MethodImpl(MethodImplOptions.NoInlining)]
virtual static NSObject _Xamarin_ConstructNSObject (NativeHandle handle) => null;
}
#endif

#if NET && !COREBUILD
[ObjectiveCTrackedType]
[SupportedOSPlatform ("ios")]
Expand All @@ -81,6 +91,9 @@ public partial class NSObject : INativeObject
#if !COREBUILD
, IEquatable<NSObject>
, IDisposable
#endif
#if NET
, INSObjectFactory
#endif
{
#if !COREBUILD
Expand Down
5 changes: 4 additions & 1 deletion src/ObjCRuntime/IManagedRegistrar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//
// Copyright 2023 Microsoft Corp


#if NET

#nullable enable
Expand Down Expand Up @@ -34,6 +33,10 @@ interface IManagedRegistrar {
// This method will be called once per assembly, and the implementation has to
// add all the interface -> wrapper type mappings to the dictionary.
void RegisterWrapperTypes (Dictionary<RuntimeTypeHandle, RuntimeTypeHandle> type);
// Create an instance of a managed NSObject subclass for an existing Objective-C object.
INativeObject? ConstructNSObject (RuntimeTypeHandle typeHandle, NativeHandle nativeHandle);
// Create an instance of a managed NSObject subclass for an existing Objective-C object.
INativeObject? ConstructINativeObject (RuntimeTypeHandle typeHandle, NativeHandle nativeHandle, bool owns);
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/ObjCRuntime/INativeObject.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#nullable enable

using System;
using System.Runtime.CompilerServices;
using Foundation;

#if !NET
Expand All @@ -15,6 +16,14 @@ NativeHandle Handle {
get;
}
#endif

#if NET
// The method will be implemented via custom linker step if the managed static registrar is used
// for classes which have an (NativeHandle, bool) or (IntPtr, bool) constructor.
// This method will be made public when the managed static registrar is used.
[MethodImpl(MethodImplOptions.NoInlining)]
internal static virtual INativeObject? _Xamarin_ConstructINativeObject (NativeHandle handle, bool owns) => null;
#endif
}

#if !COREBUILD
Expand Down
16 changes: 16 additions & 0 deletions src/ObjCRuntime/RegistrarHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,22 @@ internal static uint LookupRegisteredTypeId (Type type)
return entry.Registrar.LookupTypeId (type.TypeHandle);
}

internal static T? ConstructNSObject<T> (Type type, NativeHandle nativeHandle)
where T : class, INativeObject
{
if (!TryGetMapEntry (type.Assembly.GetName ().Name!, out var entry))
return null;
return (T?) entry.Registrar.ConstructNSObject (type.TypeHandle, nativeHandle);
}

internal static T? ConstructINativeObject<T> (Type type, NativeHandle nativeHandle, bool owns)
where T : class, INativeObject
{
if (!TryGetMapEntry (type.Assembly.GetName ().Name!, out var entry))
return null;
return (T?) entry.Registrar.ConstructINativeObject (type.TypeHandle, nativeHandle, owns);
}

// helper functions for converting between native and managed objects
static NativeHandle ManagedArrayToNSArray (object array, bool retain)
{
Expand Down
Loading

6 comments on commit 7f67aa0

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

Please sign in to comment.