Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSR: Create instances of NSObjects and INativeObjects without using reflection #18519

Merged
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
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
Loading