diff --git a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs index 22f5bd7f9..ad89dc9e2 100644 --- a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs @@ -19,28 +19,19 @@ partial class NativeAotTypeManager : JniRuntime.JniTypeManager { ["my/MainActivity"] = typeof (MainActivity), }; - public override void RegisterNativeMembers ( - JniType nativeClass, - [DynamicallyAccessedMembers (MethodsAndPrivateNested)] - Type type, - ReadOnlySpan methods) - { - Console.WriteLine ($"# jonp: RegisterNativeMembers: nativeClass={nativeClass} type=`{type}`"); - base.RegisterNativeMembers (nativeClass, type, methods); - } - - protected override IEnumerable GetTypesForSimpleReference (string jniSimpleReference) { - Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}`"); - if (typeMappings.TryGetValue (jniSimpleReference, out var target)) { - Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}` -> `{target}`"); + if (typeMappings.TryGetValue (jniSimpleReference, out var target)) yield return target; - } - foreach (var t in base.GetTypesForSimpleReference (jniSimpleReference)) { - Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}` -> `{t}`"); + foreach (var t in base.GetTypesForSimpleReference (jniSimpleReference)) yield return t; - } + } + + protected override Type? GetTypeForSimpleReference (string jniSimpleReference) + { + if (typeMappings.TryGetValue (jniSimpleReference, out var target)) + return target; + return base.GetTypeForSimpleReference (jniSimpleReference); } protected override IEnumerable GetSimpleReferences (Type type) diff --git a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs index 7d21d95e1..c8aafe9c3 100644 --- a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs @@ -19,6 +19,13 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl yield return t; } + protected override Type? GetTypeForSimpleReference (string jniSimpleReference) + { + if (typeMappings.TryGetValue (jniSimpleReference, out var target)) + return target; + return base.GetTypeForSimpleReference (jniSimpleReference); + } + protected override IEnumerable GetSimpleReferences (Type type) { return base.GetSimpleReferences (type) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs index 2ed26218c..74cb65df5 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs @@ -261,28 +261,128 @@ protected virtual IEnumerable GetSimpleReferences (Type type) static readonly string[] EmptyStringArray = Array.Empty (); static readonly Type[] EmptyTypeArray = Array.Empty (); - const string NotUsedInAndroid = "This code path is not used in Android projects."; - // FIXME: https://github.com/dotnet/java-interop/issues/1192 - [UnconditionalSuppressMessage ("Trimming", "IL3050", Justification = NotUsedInAndroid)] - static Type MakeArrayType (Type type) => - type.MakeArrayType (); + readonly struct KnownArrayTypesInfo + { + public readonly Dictionary ArrayTypes; + public readonly Dictionary JavaObjectArrayTypes; + + public KnownArrayTypesInfo (Dictionary arrayTypes, Dictionary javaObjectArrayTypes) + { + ArrayTypes = arrayTypes; + JavaObjectArrayTypes = javaObjectArrayTypes; + } + } + + static readonly Lazy KnownArrayTypes = new Lazy (InitKnownArrayTypes); + + static KnownArrayTypesInfo InitKnownArrayTypes () + { + var arrayTypes = new Dictionary (); + var javaObjectArrayTypes = new Dictionary (); + + AddKnownArrayTypes (arrayTypes, javaObjectArrayTypes); + + AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); + AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); + AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); + AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); + AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); + AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); + AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); + AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); + + return new KnownArrayTypesInfo (arrayTypes, javaObjectArrayTypes); + } + + static void AddKnownPrimitiveArrayTypes< + [DynamicallyAccessedMembers (Constructors)] + T, + [DynamicallyAccessedMembers (Constructors)] + TArray> (Dictionary arrayTypes, Dictionary javaObjectArrayTypes) + { + AddKnownArrayTypes (arrayTypes, javaObjectArrayTypes); + AddKnownArrayTypes> (arrayTypes, javaObjectArrayTypes); + AddKnownArrayTypes> (arrayTypes, javaObjectArrayTypes); + AddKnownArrayTypes (arrayTypes, javaObjectArrayTypes); + } + + static void AddKnownArrayTypes< + [DynamicallyAccessedMembers (Constructors)] + T> (Dictionary arrayTypes, Dictionary javaObjectArrayTypes) + { + arrayTypes [typeof (T)] = typeof (T[]); + arrayTypes [typeof (T[])] = typeof (T[][]); + arrayTypes [typeof (T[][])] = typeof (T[][][]); + javaObjectArrayTypes [typeof (T)] = typeof (JavaObjectArray); + javaObjectArrayTypes [typeof (JavaObjectArray)] = typeof (JavaObjectArray>); + } + + static bool TryMakeArrayType (Type type, out Type? arrayType) => + KnownArrayTypes.Value.ArrayTypes.TryGetValue (type, out arrayType); + + static bool TryMakeJavaObjectArrayType (Type type, out Type? arrayType) => + KnownArrayTypes.Value.JavaObjectArrayTypes.TryGetValue (type, out arrayType); - // FIXME: https://github.com/dotnet/java-interop/issues/1192 - [UnconditionalSuppressMessage ("Trimming", "IL2055", Justification = NotUsedInAndroid)] - [UnconditionalSuppressMessage ("Trimming", "IL3050", Justification = NotUsedInAndroid)] - static Type MakeGenericType (Type type, Type arrayType) => - type.MakeGenericType (arrayType); + static Type GetUnsupportedArrayType (Type type) => + throw new NotSupportedException ($"Array type construction for `{type}` is not supported."); + + static Type GetUnsupportedJavaObjectArrayType (Type type) => + throw new NotSupportedException ($"Generic Java array wrapper type construction for `{type}` is not supported."); - [UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "Types returned here should be preserved via other means.")] [return: DynamicallyAccessedMembers (MethodsConstructors)] public Type? GetType (JniTypeSignature typeSignature) { AssertValid (); - return GetTypes (typeSignature).FirstOrDefault (); + if (!typeSignature.IsValid || typeSignature.SimpleReference == null) + return null; + + var type = GetTypeForSimpleReference (typeSignature.SimpleReference); + if (type == null) + return null; + if (typeSignature.ArrayRank == 0) + return type; + throw new NotSupportedException ($"DAM-annotated type lookup for array signature `{typeSignature}` is not supported. Use {nameof (GetTypes)} instead."); } + [return: DynamicallyAccessedMembers (MethodsConstructors)] + protected virtual Type? GetTypeForSimpleReference (string jniSimpleReference) + { + AssertValid (); + AssertSimpleReference (jniSimpleReference); + + return jniSimpleReference switch { + "java/lang/String" => TypeOf (), + "V" => TypeOfVoid (), + "Z" => TypeOf (), + "java/lang/Boolean" => TypeOf (), + "B" => TypeOf (), + "java/lang/Byte" => TypeOf (), + "C" => TypeOf (), + "java/lang/Character" => TypeOf (), + "S" => TypeOf (), + "java/lang/Short" => TypeOf (), + "I" => TypeOf (), + "java/lang/Integer" => TypeOf (), + "J" => TypeOf (), + "java/lang/Long" => TypeOf (), + "F" => TypeOf (), + "java/lang/Float" => TypeOf (), + "D" => TypeOf (), + "java/lang/Double" => TypeOf (), + _ => null, + }; + } + + [return: DynamicallyAccessedMembers (MethodsConstructors)] + static Type TypeOf< + [DynamicallyAccessedMembers (MethodsConstructors)] + T> () => typeof (T); + + [return: DynamicallyAccessedMembers (MethodsConstructors)] + static Type TypeOfVoid () => typeof (void); + public virtual IEnumerable GetTypes (JniTypeSignature typeSignature) { AssertValid (); @@ -313,7 +413,9 @@ IEnumerable CreateGetTypesEnumerator (JniTypeSignature typeSignature) var rank = typeSignature.ArrayRank; var arrayType = type; while (rank-- > 0) { - arrayType = MakeGenericType (typeof (JavaObjectArray<>), arrayType); + arrayType = TryMakeJavaObjectArrayType (arrayType, out var nextArrayType) + ? nextArrayType ?? throw new InvalidOperationException ("Should not be reached") + : GetUnsupportedJavaObjectArrayType (arrayType); } yield return arrayType; } @@ -322,7 +424,9 @@ IEnumerable CreateGetTypesEnumerator (JniTypeSignature typeSignature) var rank = typeSignature.ArrayRank; var arrayType = type; while (rank-- > 0) { - arrayType = MakeArrayType (arrayType); + arrayType = TryMakeArrayType (arrayType, out var nextArrayType) + ? nextArrayType ?? throw new InvalidOperationException ("Should not be reached") + : GetUnsupportedArrayType (arrayType); } yield return arrayType; } @@ -345,14 +449,18 @@ IEnumerable GetPrimitiveArrayTypesForSimpleReference (JniTypeSignature typ var rank = typeSignature.ArrayRank-1; var arrayType = t; while (rank-- > 0) { - arrayType = MakeGenericType (typeof (JavaObjectArray<>), arrayType); + arrayType = TryMakeJavaObjectArrayType (arrayType, out var nextArrayType) + ? nextArrayType ?? throw new InvalidOperationException ("Should not be reached") + : GetUnsupportedJavaObjectArrayType (arrayType); } yield return arrayType; rank = typeSignature.ArrayRank-1; arrayType = t; while (rank-- > 0) { - arrayType = MakeArrayType (arrayType); + arrayType = TryMakeArrayType (arrayType, out var nextArrayType) + ? nextArrayType ?? throw new InvalidOperationException ("Should not be reached") + : GetUnsupportedArrayType (arrayType); } yield return arrayType; } @@ -392,19 +500,6 @@ IEnumerable CreateGetTypesForSimpleReferenceEnumerator (string jniSimpleRe [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."; - - // FIXME: https://github.com/dotnet/java-interop/issues/1192 - [UnconditionalSuppressMessage ("Trimming", "IL2055", Justification = makeGenericTypeMessage)] - [UnconditionalSuppressMessage ("Trimming", "IL3050", Justification = makeGenericTypeMessage)] - [return: DynamicallyAccessedMembers (Constructors)] - static Type MakeGenericType ( - [DynamicallyAccessedMembers (Constructors)] - Type type, - Type [] arguments) => - type.MakeGenericType (arguments); - var signature = type.GetCustomAttribute (); if (signature == null || signature.InvokerType == null) { return null; @@ -414,7 +509,11 @@ static Type MakeGenericType ( if (arguments.Length == 0) return signature.InvokerType; - return MakeGenericType (signature.InvokerType, arguments); +#if NET + throw new NotSupportedException ($"Generic invoker type construction for `{type}` is not supported."); +#else // NET + return signature.InvokerType.MakeGenericType (arguments); +#endif // NET } #if NET @@ -605,4 +704,3 @@ bool FindAndCallRegisterMethod ( } } } - diff --git a/src/Java.Interop/PublicAPI.Unshipped.txt b/src/Java.Interop/PublicAPI.Unshipped.txt index 359f5560e..2b1fcc8ee 100644 --- a/src/Java.Interop/PublicAPI.Unshipped.txt +++ b/src/Java.Interop/PublicAPI.Unshipped.txt @@ -4,6 +4,7 @@ static Java.Interop.JniEnvironment.EndMarshalMethod(ref Java.Interop.JniTransiti 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.JniTypeManager.GetTypeForSimpleReference(string! jniSimpleReference) -> System.Type? virtual Java.Interop.JniRuntime.JniValueManager.TryConstructPeer(Java.Interop.IJavaPeerable! self, ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type! type) -> bool 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 diff --git a/src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs b/src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs index 128ddb626..c13e21ae6 100644 --- a/src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs +++ b/src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs @@ -33,6 +33,16 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl yield return target; } + protected override Type? GetTypeForSimpleReference (string jniSimpleReference) + { + var type = base.GetTypeForSimpleReference (jniSimpleReference); + if (type != null) + return type; + if (typeMappings == null) + return null; + return typeMappings.TryGetValue (jniSimpleReference, out var target) ? target : null; + } + protected override IEnumerable GetSimpleReferences (Type type) { return base.GetSimpleReferences (type) diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs index 40da2e321..41f4aa2cf 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Diagnostics; using System.Linq; using Java.Interop; @@ -65,6 +64,12 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl #pragma warning restore CS8600 } + protected override Type? GetTypeForSimpleReference (string jniSimpleReference) + { + return base.GetTypeForSimpleReference (jniSimpleReference) ?? + (TypeMappings.TryGetValue (jniSimpleReference, out var target) ? target : null); + } + protected override IEnumerable GetSimpleReferences (Type type) { return base.GetSimpleReferences (type) @@ -91,7 +96,6 @@ IEnumerable CreateSimpleReferencesEnumerator (Type type) protected override IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimpleReference) { RequestedFallbackTypesForSimpleReference = jniSimpleReference; - Debug.WriteLine ($"# GetStaticMethodFallbackTypes (jniSimpleReference={jniSimpleReference})"); var slash = jniSimpleReference.LastIndexOf ('/'); var desugarType = slash <= 0 @@ -167,4 +171,3 @@ string GetAlternateMethodSignature () #endif // NET } } - diff --git a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs index 2fe3c2cf7..0fa2db4d7 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs @@ -26,8 +26,30 @@ public void GetInvokerType () } } +#if NET + [Test] + public void GetInvokerType_GenericType_Throws () + { + using (var vm = new MyTypeManager ()) { + Assert.Throws (() => vm.GetInvokerType (typeof (IGenericJavaInterface))); + } + } +#endif // NET + class MyTypeManager : JniRuntime.JniTypeManager { } - } -} + [JniTypeSignature ("example/GenericInterface", GenerateJavaPeer=false, InvokerType=typeof (IGenericJavaInterfaceInvoker<>))] + interface IGenericJavaInterface : IJavaPeerable { + } + + [JniTypeSignature ("example/GenericInterface", GenerateJavaPeer=false)] + class IGenericJavaInterfaceInvoker : JavaObject, IGenericJavaInterface { + + public IGenericJavaInterfaceInvoker (ref JniObjectReference reference, JniObjectReferenceOptions options) + : base (ref reference, options) + { + } + } + } +} diff --git a/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs b/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs index 3ecde892f..a028aef2e 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs @@ -137,6 +137,11 @@ static void AssertGetJniTypeInfoForType (Type type, string jniType, bool isKeywo Assert.AreEqual (typeof (double), GetType ("D")); Assert.AreEqual (typeof (string), GetType ("java/lang/String")); Assert.AreEqual (typeof (float?), GetType ("java/lang/Float")); +#if !__ANDROID__ + Assert.AreEqual (typeof (GenericHolder<>), GetType (GenericHolder.JniTypeName)); +#endif // !__ANDROID__ + Assert.Throws (() => GetType ("[I")); + Assert.Throws (() => GetType ("[Ljava/lang/String;")); Assert.AreEqual (null, GetType ("com/example/does/not/exist")); Assert.AreEqual (null, GetType ("Lcom/example/does/not/exist;")); Assert.AreEqual (null, GetType ("[Lcom/example/does/not/exist;")); @@ -246,4 +251,3 @@ class GenericHolder : JavaObject { public T Value {get; set;} } } -