diff --git a/src/Java.Interop/Documentation/Java.Interop/JniRuntime.JniTypeManager.xml b/src/Java.Interop/Documentation/Java.Interop/JniRuntime.JniTypeManager.xml
new file mode 100644
index 000000000..5adca3ece
--- /dev/null
+++ b/src/Java.Interop/Documentation/Java.Interop/JniRuntime.JniTypeManager.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ Manages bound Java types.
+
+
+
+
+
+ Gets the Invoker type for
+
+
+ An Invoker type is a concrete type which can be constructed,
+ which is used to invoke instances of abstract type that cannot be constructed.
+ For example, the interface type Java.Lang.IRunnable cannot be constructed,
+ but if a java.lang.Runnable instance enters managed code,
+ a Invoker must be constructed around the instance so that it may be used.
+
+
+
+ If is an interface or abstract class, returns the
+ type which should be constructed around instances of .
+ If no such type exists, or if is a concrete type,
+ then is returned.
+
+
+
diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs
index 5f93dbe2b..0399a70e4 100644
--- a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs
+++ b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs
@@ -79,8 +79,10 @@ public override string ToString ()
}
#endif // NET
+ ///
public partial class JniTypeManager : IDisposable, ISetRuntime {
+ internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
internal const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods;
internal const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes;
internal const DynamicallyAccessedMemberTypes MethodsConstructors = MethodsAndPrivateNested | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
@@ -385,6 +387,49 @@ IEnumerable CreateGetTypesForSimpleReferenceEnumerator (string jniSimpleRe
yield break;
}
+ ///
+ [return: DynamicallyAccessedMembers (Constructors)]
+ public Type? GetInvokerType (
+ [DynamicallyAccessedMembers (Constructors)]
+ Type type)
+ {
+ if (type.IsAbstract || type.IsInterface) {
+ return GetInvokerTypeCore (type);
+ }
+ return null;
+ }
+
+ [return: DynamicallyAccessedMembers (Constructors)]
+ protected virtual Type? GetInvokerTypeCore (
+ [DynamicallyAccessedMembers (Constructors)]
+ Type type)
+ {
+ // https://github.com/xamarin/xamarin-android/blob/5472eec991cc075e4b0c09cd98a2331fb93aa0f3/src/Microsoft.Android.Sdk.ILLink/MarkJavaObjects.cs#L176-L186
+ const string makeGenericTypeMessage = "Generic 'Invoker' types are preserved by the MarkJavaObjects trimmer step.";
+
+ [UnconditionalSuppressMessage ("Trimming", "IL2055", Justification = makeGenericTypeMessage)]
+ [return: DynamicallyAccessedMembers (Constructors)]
+ static Type MakeGenericType (
+ [DynamicallyAccessedMembers (Constructors)]
+ Type type,
+ Type [] arguments) =>
+ // FIXME: https://github.com/dotnet/java-interop/issues/1192
+ #pragma warning disable IL3050
+ type.MakeGenericType (arguments);
+ #pragma warning restore IL3050
+
+ var signature = type.GetCustomAttribute ();
+ if (signature == null || signature.InvokerType == null) {
+ return null;
+ }
+
+ Type[] arguments = type.GetGenericArguments ();
+ if (arguments.Length == 0)
+ return signature.InvokerType;
+
+ return MakeGenericType (signature.InvokerType, arguments);
+ }
+
#if NET
public IReadOnlyList? GetStaticMethodFallbackTypes (string jniSimpleReference)
diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs
index ad8d04c35..eb9e23790 100644
--- a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs
+++ b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs
@@ -336,31 +336,21 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type)
JniObjectReference.Dispose (ref targetClass);
- var ctor = GetPeerConstructor (ref refClass, targetType);
- if (ctor == null) {
+ var peer = CreatePeerInstance (ref refClass, targetType, ref reference, transfer);
+ if (peer == null) {
throw new NotSupportedException (string.Format ("Could not find an appropriate constructable wrapper type for Java type '{0}', targetType='{1}'.",
JniEnvironment.Types.GetJniTypeNameFromInstance (reference), targetType));
}
-
- var acts = new object[] {
- reference,
- transfer,
- };
- try {
- var peer = (IJavaPeerable) ctor.Invoke (acts);
- peer.SetJniManagedPeerState (peer.JniManagedPeerState | JniManagedPeerStates.Replaceable);
- return peer;
- } finally {
- reference = (JniObjectReference) acts [0];
- }
+ peer.SetJniManagedPeerState (peer.JniManagedPeerState | JniManagedPeerStates.Replaceable);
+ return peer;
}
- static readonly Type ByRefJniObjectReference = typeof (JniObjectReference).MakeByRefType ();
-
- ConstructorInfo? GetPeerConstructor (
+ IJavaPeerable? CreatePeerInstance (
ref JniObjectReference klass,
[DynamicallyAccessedMembers (Constructors)]
- Type fallbackType)
+ Type fallbackType,
+ ref JniObjectReference reference,
+ JniObjectReferenceOptions transfer)
{
var jniTypeName = JniEnvironment.Types.GetJniTypeNameFromClass (klass);
@@ -373,11 +363,11 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type)
type = Runtime.TypeManager.GetType (sig);
if (type != null) {
- var ctor = GetActivationConstructor (type);
+ var peer = TryCreatePeerInstance (ref reference, transfer, type);
- if (ctor != null) {
+ if (peer != null) {
JniObjectReference.Dispose (ref klass);
- return ctor;
+ return peer;
}
}
@@ -391,51 +381,41 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type)
}
JniObjectReference.Dispose (ref klass, JniObjectReferenceOptions.CopyAndDispose);
- return GetActivationConstructor (fallbackType);
+ return TryCreatePeerInstance (ref reference, transfer, fallbackType);
}
- static ConstructorInfo? GetActivationConstructor (
+ IJavaPeerable? TryCreatePeerInstance (
+ ref JniObjectReference reference,
+ JniObjectReferenceOptions options,
[DynamicallyAccessedMembers (Constructors)]
Type type)
{
- if (type.IsAbstract || type.IsInterface) {
- type = GetInvokerType (type) ?? type;
- }
- foreach (var c in type.GetConstructors (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) {
- var p = c.GetParameters ();
- if (p.Length == 2 && p [0].ParameterType == ByRefJniObjectReference && p [1].ParameterType == typeof (JniObjectReferenceOptions))
- return c;
- }
- return null;
+ type = Runtime.TypeManager.GetInvokerType (type) ?? type;
+ return TryCreatePeer (ref reference, options, type);
}
- [return: DynamicallyAccessedMembers (Constructors)]
- static Type? GetInvokerType (Type type)
- {
- // https://github.com/xamarin/xamarin-android/blob/5472eec991cc075e4b0c09cd98a2331fb93aa0f3/src/Microsoft.Android.Sdk.ILLink/MarkJavaObjects.cs#L176-L186
- const string makeGenericTypeMessage = "Generic 'Invoker' types are preserved by the MarkJavaObjects trimmer step.";
-
- [UnconditionalSuppressMessage ("Trimming", "IL2055", Justification = makeGenericTypeMessage)]
- [return: DynamicallyAccessedMembers (Constructors)]
- static Type MakeGenericType (
- [DynamicallyAccessedMembers (Constructors)]
- Type type,
- Type [] arguments) =>
- // FIXME: https://github.com/dotnet/java-interop/issues/1192
- #pragma warning disable IL3050
- type.MakeGenericType (arguments);
- #pragma warning restore IL3050
-
- var signature = type.GetCustomAttribute ();
- if (signature == null || signature.InvokerType == null) {
- return null;
- }
+ const BindingFlags ActivationConstructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
+ static readonly Type ByRefJniObjectReference = typeof (JniObjectReference).MakeByRefType ();
+ static readonly Type[] JIConstructorSignature = new Type [] { ByRefJniObjectReference, typeof (JniObjectReferenceOptions) };
- Type[] arguments = type.GetGenericArguments ();
- if (arguments.Length == 0)
- return signature.InvokerType;
- return MakeGenericType (signature.InvokerType, arguments);
+ protected virtual IJavaPeerable? TryCreatePeer (
+ ref JniObjectReference reference,
+ JniObjectReferenceOptions options,
+ [DynamicallyAccessedMembers (Constructors)]
+ Type type)
+ {
+ var c = type.GetConstructor (ActivationConstructorBindingFlags, null, JIConstructorSignature, null);
+ if (c != null) {
+ var args = new object[] {
+ reference,
+ options,
+ };
+ var p = (IJavaPeerable) c.Invoke (args);
+ reference = (JniObjectReference) args [0];
+ return p;
+ }
+ return null;
}
public object? CreateValue (
diff --git a/src/Java.Interop/PublicAPI.Unshipped.txt b/src/Java.Interop/PublicAPI.Unshipped.txt
index 5a788dfa6..f25b9f6bb 100644
--- a/src/Java.Interop/PublicAPI.Unshipped.txt
+++ b/src/Java.Interop/PublicAPI.Unshipped.txt
@@ -3,8 +3,11 @@ static Java.Interop.JniEnvironment.BeginMarshalMethod(nint jnienv, out Java.Inte
static Java.Interop.JniEnvironment.EndMarshalMethod(ref Java.Interop.JniTransition transition) -> void
virtual Java.Interop.JniRuntime.OnEnterMarshalMethod() -> void
virtual Java.Interop.JniRuntime.OnUserUnhandledException(ref Java.Interop.JniTransition transition, System.Exception! e) -> void
+virtual Java.Interop.JniRuntime.JniTypeManager.GetInvokerTypeCore(System.Type! type) -> System.Type?
+virtual Java.Interop.JniRuntime.JniValueManager.TryCreatePeer(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type! type) -> Java.Interop.IJavaPeerable?
Java.Interop.JavaException.JavaException(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions transfer, Java.Interop.JniObjectReference throwableOverride) -> void
Java.Interop.JavaException.SetJavaStackTrace(Java.Interop.JniObjectReference peerReferenceOverride = default(Java.Interop.JniObjectReference)) -> void
+Java.Interop.JniRuntime.JniTypeManager.GetInvokerType(System.Type! type) -> System.Type?
Java.Interop.JniRuntime.JniValueManager.GetPeer(Java.Interop.JniObjectReference reference, System.Type? targetType = null) -> Java.Interop.IJavaPeerable?
Java.Interop.JniTypeSignatureAttribute.InvokerType.get -> System.Type?
Java.Interop.JniTypeSignatureAttribute.InvokerType.set -> void
diff --git a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs
new file mode 100644
index 000000000..2fe3c2cf7
--- /dev/null
+++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Reflection;
+using System.Collections.Generic;
+
+using Java.Interop;
+
+using NUnit.Framework;
+
+namespace Java.InteropTests {
+
+ [TestFixture]
+ public class JniRuntimeJniTypeManagerTests : JavaVMFixture {
+
+ [Test]
+ public void GetInvokerType ()
+ {
+ using (var vm = new MyTypeManager ()) {
+ // Concrete type; no invoker
+ Assert.IsNull (vm.GetInvokerType (typeof (JavaObject)));
+
+ // Not a bound abstract Java type; no invoker
+ Assert.IsNull (vm.GetInvokerType (typeof (System.ICloneable)));
+
+ // Bound abstract Java type; has an invoker
+ Assert.AreSame (typeof (IJavaInterfaceInvoker), vm.GetInvokerType (typeof (IJavaInterface)));
+ }
+ }
+
+ class MyTypeManager : JniRuntime.JniTypeManager {
+ }
+ }
+}
+