From 58f0dbdc00918e39b18a90097d30856dd3c51234 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 4 Jun 2026 16:01:19 +0200 Subject: [PATCH 1/2] WIP --- .../NativeAotTypeManager.cs | 70 +++- samples/Hello-NativeAOTFromJNI/ManagedType.cs | 2 +- .../NativeAotTypeManager.cs | 89 ++++- src/Java.Interop/GlobalSuppressions.cs | 1 + .../Java.Interop/JavaPrimitiveArrays.cs | 3 +- .../Java.Interop/JavaPrimitiveArrays.tt | 2 +- .../Java.Interop/JniRuntime.JniTypeManager.cs | 357 +++++++++++++----- .../JniRuntime.JniValueManager.cs | 20 +- src/Java.Interop/Java.Interop/JniRuntime.cs | 2 +- src/Java.Interop/PublicAPI.Shipped.txt | 20 +- src/Java.Interop/PublicAPI.Unshipped.txt | 27 +- .../Java.Interop/JreTypeManager.cs | 13 +- .../Java.Interop/JavaVMFixture.cs | 12 +- .../JniRuntime.JniTypeManagerTests.cs | 33 +- .../Java.Interop/JniRuntimeTest.cs | 7 +- .../Java.Interop/JniTypeManagerTests.cs | 6 +- 16 files changed, 521 insertions(+), 143 deletions(-) diff --git a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs index 22f5bd7f9..e87001a4a 100644 --- a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs @@ -6,6 +6,7 @@ namespace Java.Interop.Samples.NativeAotFromAndroid; partial class NativeAotTypeManager : JniRuntime.JniTypeManager { + internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; internal const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods; internal const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; @@ -26,9 +27,27 @@ public override void RegisterNativeMembers ( ReadOnlySpan methods) { Console.WriteLine ($"# jonp: RegisterNativeMembers: nativeClass={nativeClass} type=`{type}`"); - base.RegisterNativeMembers (nativeClass, type, methods); + if (!methods.IsEmpty) + throw new NotSupportedException ($"Could not register native members for type '{type.FullName}'."); } + public override void RegisterNativeMembers ( + JniType nativeClass, + [DynamicallyAccessedMembers (MethodsAndPrivateNested)] + Type type, + string? methods) + { + RegisterNativeMembers (nativeClass, type, methods.AsSpan ()); + } + + protected override string? GetSimpleReference (Type type) + { + foreach (var e in typeMappings) { + if (e.Value == type) + return e.Key; + } + return null; + } protected override IEnumerable GetTypesForSimpleReference (string jniSimpleReference) { @@ -37,16 +56,18 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}` -> `{target}`"); yield return target; } - foreach (var t in base.GetTypesForSimpleReference (jniSimpleReference)) { - Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}` -> `{t}`"); - yield return t; - } + } + + protected override Type? GetTypeForSimpleReference (string jniSimpleReference) + { + if (typeMappings.TryGetValue (jniSimpleReference, out var target)) + return target; + return null; } protected override IEnumerable GetSimpleReferences (Type type) { - return base.GetSimpleReferences (type) - .Concat (CreateSimpleReferencesEnumerator (type)); + return CreateSimpleReferencesEnumerator (type); } IEnumerable CreateSimpleReferencesEnumerator (Type type) @@ -58,4 +79,39 @@ IEnumerable CreateSimpleReferencesEnumerator (Type type) yield return e.Key; } } + + public override IEnumerable GetTypes (JniTypeSignature typeSignature) + { + if (!typeSignature.IsValid || typeSignature.ArrayRank != 0 || typeSignature.SimpleReference == null) + return []; + return GetTypesForSimpleReference (typeSignature.SimpleReference); + } + + public override IEnumerable GetReflectionConstructibleTypes (JniTypeSignature typeSignature) + { + foreach (var type in GetTypes (typeSignature)) { + yield return new ReflectionConstructibleType (type); + } + } + + protected override Type? GetInvokerTypeCore ([DynamicallyAccessedMembers (Constructors)] Type type) => null; + + protected override JniTypeSignature GetTypeSignatureCore (Type type) + { + var simpleReference = GetSimpleReference (type); + return simpleReference == null ? default : new JniTypeSignature (simpleReference, 0, false); + } + + protected override IEnumerable GetTypeSignaturesCore (Type type) + { + var signature = GetTypeSignatureCore (type); + if (signature.IsValid) + yield return signature; + } + + protected override IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimpleReference) => null; + + protected override string? GetReplacementTypeCore (string jniSimpleReference) => null; + + protected override JniRuntime.ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSimpleReference, string jniMethodName, string jniMethodSignature) => null; } diff --git a/samples/Hello-NativeAOTFromJNI/ManagedType.cs b/samples/Hello-NativeAOTFromJNI/ManagedType.cs index 2c2d1f42c..d3451f352 100644 --- a/samples/Hello-NativeAOTFromJNI/ManagedType.cs +++ b/samples/Hello-NativeAOTFromJNI/ManagedType.cs @@ -38,7 +38,7 @@ static IntPtr n_GetString (IntPtr jnienv, IntPtr n_self) } [JniAddNativeMethodRegistration] - static void RegisterNativeMembers (JniNativeMethodRegistrationArguments args) + internal static void RegisterNativeMembers (JniNativeMethodRegistrationArguments args) { args.AddRegistrations (new [] { new JniNativeMethodRegistration ("n_GetString", "()Ljava/lang/String;", new _JniMarshal_PP_L (n_GetString)), diff --git a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs index 7d21d95e1..709098bd9 100644 --- a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs @@ -1,28 +1,47 @@ using Java.Interop; +using System.Diagnostics.CodeAnalysis; namespace Hello_NativeAOTFromJNI; class NativeAotTypeManager : JniRuntime.JniTypeManager { + internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; + internal const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods; + internal const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; #pragma warning disable IL2026 Dictionary typeMappings = new () { [Example.ManagedType.JniTypeName] = typeof (Example.ManagedType), + [Java.Lang.Object.JniTypeName] = typeof (Java.Lang.Object), + [Java.Lang.String.JniTypeName] = typeof (Java.Lang.String), }; #pragma warning restore IL2026 + protected override string? GetSimpleReference (Type type) + { + foreach (var e in typeMappings) { + if (e.Value == type) + return e.Key; + } + return null; + } + protected override IEnumerable GetTypesForSimpleReference (string jniSimpleReference) { if (typeMappings.TryGetValue (jniSimpleReference, out var target)) yield return target; - 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 null; } protected override IEnumerable GetSimpleReferences (Type type) { - return base.GetSimpleReferences (type) - .Concat (CreateSimpleReferencesEnumerator (type)); + return CreateSimpleReferencesEnumerator (type); } IEnumerable CreateSimpleReferencesEnumerator (Type type) @@ -34,4 +53,66 @@ IEnumerable CreateSimpleReferencesEnumerator (Type type) yield return e.Key; } } + + public override IEnumerable GetTypes (JniTypeSignature typeSignature) + { + if (!typeSignature.IsValid || typeSignature.ArrayRank != 0 || typeSignature.SimpleReference == null) + return []; + return GetTypesForSimpleReference (typeSignature.SimpleReference); + } + + public override IEnumerable GetReflectionConstructibleTypes (JniTypeSignature typeSignature) + { + foreach (var type in GetTypes (typeSignature)) { + yield return new ReflectionConstructibleType (type); + } + } + + protected override Type? GetInvokerTypeCore ([DynamicallyAccessedMembers (Constructors)] Type type) => null; + + protected override JniTypeSignature GetTypeSignatureCore (Type type) + { + var simpleReference = GetSimpleReference (type); + return simpleReference == null ? default : new JniTypeSignature (simpleReference, 0, false); + } + + protected override IEnumerable GetTypeSignaturesCore (Type type) + { + var signature = GetTypeSignatureCore (type); + if (signature.IsValid) + yield return signature; + } + + protected override IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimpleReference) => null; + + protected override string? GetReplacementTypeCore (string jniSimpleReference) => null; + + protected override JniRuntime.ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSimpleReference, string jniMethodName, string jniMethodSignature) => null; + + public override void RegisterNativeMembers ( + JniType nativeClass, + [DynamicallyAccessedMembers (MethodsAndPrivateNested)] + Type type, + ReadOnlySpan methods) + { + if (type != typeof (Example.ManagedType)) { + if (!methods.IsEmpty) + throw new NotSupportedException ($"Could not register native members for type '{type.FullName}'."); + return; + } + + var registrations = new List (); + Example.ManagedType.RegisterNativeMembers (new JniNativeMethodRegistrationArguments (registrations, null)); + if (registrations.Count > 0) + nativeClass.RegisterNativeMethods (registrations.ToArray ()); + } + + public override void RegisterNativeMembers ( + JniType nativeClass, + [DynamicallyAccessedMembers (MethodsAndPrivateNested)] + Type type, + string? methods) + { + RegisterNativeMembers (nativeClass, type, methods.AsSpan ()); + } } diff --git a/src/Java.Interop/GlobalSuppressions.cs b/src/Java.Interop/GlobalSuppressions.cs index 64ae59b42..775592df2 100644 --- a/src/Java.Interop/GlobalSuppressions.cs +++ b/src/Java.Interop/GlobalSuppressions.cs @@ -25,6 +25,7 @@ [assembly: SuppressMessage ("Design", "CA1034:Nested types should not be visible", Justification = "Deliberate choice to 'hide' these types from code completion for `Java.Interop.`.", Scope = "type", Target = "~T:Java.Interop.JniRuntime.JniValueManager")] [assembly: SuppressMessage ("Design", "CA1034:Nested types should not be visible", Justification = "Deliberate choice to 'hide' these types from code completion for `Java.Interop.`.", Scope = "type", Target = "~T:Java.Interop.JniEnvironment.References")] [assembly: SuppressMessage ("Design", "CA1034:Nested types should not be visible", Justification = "Deliberate choice to 'hide' these types from code completion for `Java.Interop.`.", Scope = "type", Target = "~T:Java.Interop.JniRuntime.JniTypeManager")] +[assembly: SuppressMessage ("Design", "CA1034:Nested types should not be visible", Justification = "Deliberate choice to 'hide' these types from code completion for `Java.Interop.`.", Scope = "type", Target = "~T:Java.Interop.JniRuntime.DynamicJniTypeManager")] [assembly: SuppressMessage ("Design", "CA1034:Nested types should not be visible", Justification = "Deliberate choice to 'hide' these types from code completion for `Java.Interop.`.", Scope = "type", Target = "~T:Java.Interop.JniPeerMembers.JniInstanceMethods")] [assembly: SuppressMessage ("Design", "CA1034:Nested types should not be visible", Justification = "Deliberate choice to 'hide' these types from code completion for `Java.Interop.`.", Scope = "type", Target = "~T:Java.Interop.JniPeerMembers.JniInstanceFields")] [assembly: SuppressMessage ("Design", "CA1034:Nested types should not be visible", Justification = "Deliberate choice to 'hide' these types from code completion for `Java.Interop.`.", Scope = "type", Target = "~T:Java.Interop.JniRuntime.CreationOptions")] diff --git a/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs b/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs index dad9743ed..d18e6dea6 100644 --- a/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs +++ b/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs @@ -13,8 +13,7 @@ namespace Java.Interop { partial class JniRuntime { - partial class JniTypeManager { - + partial class DynamicJniTypeManager { readonly struct JniPrimitiveArrayInfo { public readonly JniTypeSignature JniTypeSignature; public readonly Type PrimitiveType; diff --git a/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt b/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt index 086f6bf07..8a3a6d31b 100644 --- a/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt +++ b/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt @@ -30,7 +30,7 @@ namespace Java.Interop { #> partial class JniRuntime { - partial class JniTypeManager { + partial class DynamicJniTypeManager { readonly struct JniPrimitiveArrayInfo { public readonly JniTypeSignature JniTypeSignature; diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs index e0bab4691..4896211ce 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs @@ -80,7 +80,7 @@ public override string ToString () #endif // NET /// - public partial class JniTypeManager : IDisposable, ISetRuntime { + public abstract partial class JniTypeManager : IDisposable, ISetRuntime { internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; internal const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods; @@ -112,7 +112,7 @@ protected virtual void Dispose (bool disposing) } [MethodImpl (MethodImplOptions.AggressiveInlining)] - void AssertValid () + private protected void AssertValid () { if (!disposed) return; @@ -146,7 +146,135 @@ public JniTypeSignature GetTypeSignature (Type type) if (type == null) throw new ArgumentNullException (nameof (type)); + return GetTypeSignatureCore (type); + } + + protected abstract JniTypeSignature GetTypeSignatureCore (Type type); + + // NOTE: This method needs to be kept in sync with GetTypeSignature() + public IEnumerable GetTypeSignatures (Type type) + { + AssertValid (); + + if (type == null) + return []; + + return GetTypeSignaturesCore (type); + } + + protected abstract IEnumerable GetTypeSignaturesCore (Type type); + + [return: DynamicallyAccessedMembers (MethodsConstructors)] + public Type? GetType (JniTypeSignature typeSignature) + { + AssertValid (); + + 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."); + } + + protected abstract string? GetSimpleReference (Type type); + protected abstract IEnumerable GetSimpleReferences (Type type); + [return: DynamicallyAccessedMembers (MethodsConstructors)] + protected abstract Type? GetTypeForSimpleReference (string jniSimpleReference); + public abstract IEnumerable GetTypes (JniTypeSignature typeSignature); + + public abstract IEnumerable GetReflectionConstructibleTypes (JniTypeSignature typeSignature); + + public class ReflectionConstructibleType + { + public ReflectionConstructibleType ( + [DynamicallyAccessedMembers (Constructors)] + Type type) + { + Type = type; + } + + [DynamicallyAccessedMembers (Constructors)] + public Type Type { get; } + } + + protected abstract IEnumerable GetTypesForSimpleReference (string jniSimpleReference); + + /// + [return: DynamicallyAccessedMembers (Constructors)] + public Type? GetInvokerType ( + [DynamicallyAccessedMembers (Constructors)] + Type type) + { + if (type.IsAbstract || type.IsInterface) { + return GetInvokerTypeCore (type); + } + return null; + } + + [return: DynamicallyAccessedMembers (Constructors)] + protected abstract Type? GetInvokerTypeCore ([DynamicallyAccessedMembers (Constructors)] Type type); +#if NET + protected abstract IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimple); + + public string? GetReplacementType (string jniSimpleReference) + { + AssertValid (); + AssertSimpleReference (jniSimpleReference, nameof (jniSimpleReference)); + + return GetReplacementTypeCore (jniSimpleReference); + } + + protected abstract string? GetReplacementTypeCore (string jniSimpleReference); + + public IReadOnlyList? GetStaticMethodFallbackTypes (string jniSimpleReference) + { + AssertValid (); + AssertSimpleReference (jniSimpleReference, nameof (jniSimpleReference)); + + return GetStaticMethodFallbackTypesCore (jniSimpleReference); + } + + public ReplacementMethodInfo? GetReplacementMethodInfo (string jniSimpleReference, string jniMethodName, string jniMethodSignature) + { + AssertValid (); + AssertSimpleReference (jniSimpleReference, nameof (jniSimpleReference)); + if (string.IsNullOrEmpty (jniMethodName)) { + throw new ArgumentNullException (nameof (jniMethodName)); + } + if (string.IsNullOrEmpty (jniMethodSignature)) { + throw new ArgumentNullException (nameof (jniMethodSignature)); + } + + return GetReplacementMethodInfoCore (jniSimpleReference, jniMethodName, jniMethodSignature); + } + + protected abstract ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSimpleReference, string jniMethodName, string jniMethodSignature); + + public abstract void RegisterNativeMembers ( + JniType nativeClass, + [DynamicallyAccessedMembers (MethodsAndPrivateNested)] + Type type, + ReadOnlySpan methods); +#endif +#if NET + [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] +#endif // NET + public abstract void RegisterNativeMembers ( + JniType nativeClass, + [DynamicallyAccessedMembers (MethodsAndPrivateNested)] + Type type, + string? methods); + } + [RequiresUnreferencedCode ("The default JniTypeManager implementation does not support trimming. Use a custom JniTypeManager implementation that supports trimming, or ensure that all types used with the default JniTypeManager are preserved.")] + public partial class DynamicJniTypeManager : JniTypeManager { + + protected override JniTypeSignature GetTypeSignatureCore (Type type) + { type = GetUnderlyingType (type, out int rank); JniTypeSignature signature = JniTypeSignature.Empty; @@ -175,14 +303,8 @@ public JniTypeSignature GetTypeSignature (Type type) return default; } - // NOTE: This method needs to be kept in sync with GetTypeSignature() - public IEnumerable GetTypeSignatures (Type type) + protected override IEnumerable GetTypeSignaturesCore (Type type) { - AssertValid (); - - if (type == null) - yield break; - type = GetUnderlyingType (type, out int rank); var signature = JniTypeSignature.Empty; @@ -231,13 +353,13 @@ static Type GetUnderlyingType (Type type, out int rank) } // `type` will NOT be an array type. - protected virtual string? GetSimpleReference (Type type) + protected override string? GetSimpleReference (Type type) { return GetSimpleReferences (type).FirstOrDefault (); } // `type` will NOT be an array type. - protected virtual IEnumerable GetSimpleReferences (Type type) + protected override IEnumerable GetSimpleReferences (Type type) { AssertValid (); @@ -261,29 +383,113 @@ 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; - // 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); + 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); + + 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) + protected override Type? GetTypeForSimpleReference (string jniSimpleReference) { AssertValid (); + AssertSimpleReference (jniSimpleReference); - return GetTypes (typeSignature).FirstOrDefault (); + 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, + }; } - public virtual IEnumerable GetTypes (JniTypeSignature typeSignature) + [return: DynamicallyAccessedMembers (MethodsConstructors)] + static Type TypeOf< + [DynamicallyAccessedMembers (MethodsConstructors)] + T> () => typeof (T); + + [return: DynamicallyAccessedMembers (MethodsConstructors)] + static Type TypeOfVoid () => typeof (void); + + public override IEnumerable GetTypes (JniTypeSignature typeSignature) { AssertValid (); @@ -292,11 +498,18 @@ public virtual IEnumerable GetTypes (JniTypeSignature typeSignature) return CreateGetTypesEnumerator (typeSignature); } + public override IEnumerable GetReflectionConstructibleTypes (JniTypeSignature typeSignature) + { + foreach (var type in GetTypes (typeSignature)) { + yield return new ReflectionConstructibleType (type); + } + } + IEnumerable CreateGetTypesEnumerator (JniTypeSignature typeSignature) { if (!typeSignature.IsValid) yield break; - foreach (var type in GetTypesForSimpleReference (typeSignature.SimpleReference ?? throw new InvalidOperationException ("Should not be reached")) ){ + foreach (var type in GetTypesForSimpleReference (typeSignature.SimpleReference ?? throw new InvalidOperationException ("Should not be reached"))) { if (typeSignature.ArrayRank == 0) { yield return type; continue; @@ -313,7 +526,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 +537,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,20 +562,24 @@ 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; } } - protected virtual IEnumerable GetTypesForSimpleReference (string jniSimpleReference) + protected override IEnumerable GetTypesForSimpleReference (string jniSimpleReference) { AssertValid (); AssertSimpleReference (jniSimpleReference); @@ -378,36 +599,11 @@ 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 ( + protected override 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."; - - // 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; @@ -417,48 +613,22 @@ static Type MakeGenericType ( if (arguments.Length == 0) return signature.InvokerType; - return MakeGenericType (signature.InvokerType, arguments); - } - #if NET - - public IReadOnlyList? GetStaticMethodFallbackTypes (string jniSimpleReference) - { - AssertValid (); - AssertSimpleReference (jniSimpleReference, nameof (jniSimpleReference)); - - return GetStaticMethodFallbackTypesCore (jniSimpleReference); - } - - protected virtual IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimple) => null; - - public string? GetReplacementType (string jniSimpleReference) - { - AssertValid (); - AssertSimpleReference (jniSimpleReference, nameof (jniSimpleReference)); - - return GetReplacementTypeCore (jniSimpleReference); + throw new NotSupportedException ($"Generic invoker type construction for `{type}` is not supported."); +#else // NET + return signature.InvokerType.MakeGenericType (arguments); +#endif // NET } - protected virtual string? GetReplacementTypeCore (string jniSimpleReference) => null; +#if NET - public ReplacementMethodInfo? GetReplacementMethodInfo (string jniSimpleReference, string jniMethodName, string jniMethodSignature) - { - AssertValid (); - AssertSimpleReference (jniSimpleReference, nameof (jniSimpleReference)); - if (string.IsNullOrEmpty (jniMethodName)) { - throw new ArgumentNullException (nameof (jniMethodName)); - } - if (string.IsNullOrEmpty (jniMethodSignature)) { - throw new ArgumentNullException (nameof (jniMethodSignature)); - } + protected override IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimple) => null; - return GetReplacementMethodInfoCore (jniSimpleReference, jniMethodName, jniMethodSignature); - } + protected override string? GetReplacementTypeCore (string jniSimpleReference) => null; - protected virtual ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSimpleReference, string jniMethodName, string jniMethodSignature) => null; + protected override ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSimpleReference, string jniMethodName, string jniMethodSignature) => null; - public virtual void RegisterNativeMembers ( + public override void RegisterNativeMembers ( JniType nativeClass, [DynamicallyAccessedMembers (MethodsAndPrivateNested)] Type type, @@ -486,7 +656,7 @@ protected bool TryRegisterNativeMembers ( #if NET [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] #endif // NET - public virtual void RegisterNativeMembers ( + public override void RegisterNativeMembers ( JniType nativeClass, [DynamicallyAccessedMembers (MethodsAndPrivateNested)] Type type, @@ -605,6 +775,7 @@ bool FindAndCallRegisterMethod ( return found; } + } } } diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs index c9a0fe54b..5913b2f80 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs @@ -407,13 +407,12 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) return TryCreatePeerInstance (ref reference, transfer, targetType); - [UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "Types returned here should be preserved via other means.")] [return: DynamicallyAccessedMembers (Constructors)] Type? GetTypeAssignableTo (JniTypeSignature sig, Type targetType) { - foreach (var t in Runtime.TypeManager.GetTypes (sig)) { - if (targetType.IsAssignableFrom (t)) { - return t; + foreach (var t in Runtime.TypeManager.GetReflectionConstructibleTypes (sig)) { + if (targetType.IsAssignableFrom (t.Type)) { + return t.Type; } } return null; @@ -428,7 +427,9 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) { type = Runtime.TypeManager.GetInvokerType (type) ?? type; - var self = GetUninitializedObject (type); + var self = (IJavaPeerable) System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject (type); + self.SetJniManagedPeerState (JniManagedPeerStates.Replaceable | JniManagedPeerStates.Activatable); + var constructed = false; try { constructed = TryConstructPeer (self, ref reference, options, type); @@ -439,15 +440,6 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) } } return self; - - static IJavaPeerable GetUninitializedObject ( - [DynamicallyAccessedMembers (Constructors)] - Type type) - { - var v = (IJavaPeerable) System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject (type); - v.SetJniManagedPeerState (JniManagedPeerStates.Replaceable | JniManagedPeerStates.Activatable); - return v; - } } const BindingFlags ActivationConstructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; diff --git a/src/Java.Interop/Java.Interop/JniRuntime.cs b/src/Java.Interop/Java.Interop/JniRuntime.cs index b9b6453a8..7942c07d2 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.cs @@ -202,7 +202,7 @@ protected JniRuntime (CreationOptions options) SetValueManager (options); ObjectReferenceManager = SetRuntime (options.ObjectReferenceManager ?? throw new NotSupportedException ($"Please set {nameof (CreationOptions)}.{nameof (options.ObjectReferenceManager)}!")); - TypeManager = SetRuntime (options.TypeManager ?? new JniTypeManager ()); + TypeManager = SetRuntime (options.TypeManager ?? throw new NotSupportedException ($"Please set {nameof (CreationOptions)}.{nameof (options.TypeManager)}!")); if (Interlocked.CompareExchange (ref current, this, null) != null) { Debug.WriteLine ("WARNING: More than one JniRuntime instance created. This is DOOMED TO FAIL."); diff --git a/src/Java.Interop/PublicAPI.Shipped.txt b/src/Java.Interop/PublicAPI.Shipped.txt index e1a948397..bd748c279 100644 --- a/src/Java.Interop/PublicAPI.Shipped.txt +++ b/src/Java.Interop/PublicAPI.Shipped.txt @@ -418,8 +418,6 @@ Java.Interop.JniRuntime.JniTypeManager.GetTypeSignature(System.Type! type) -> Ja Java.Interop.JniRuntime.JniTypeManager.GetTypeSignatures(System.Type! type) -> System.Collections.Generic.IEnumerable! Java.Interop.JniRuntime.JniTypeManager.JniTypeManager() -> void Java.Interop.JniRuntime.JniTypeManager.Runtime.get -> Java.Interop.JniRuntime! -Java.Interop.JniRuntime.JniTypeManager.TryRegisterNativeMembers(Java.Interop.JniType! nativeClass, System.Type! type, string? methods) -> bool -Java.Interop.JniRuntime.JniTypeManager.TryRegisterNativeMembers(Java.Interop.JniType! nativeClass, System.Type! type, System.ReadOnlySpan methods) -> bool Java.Interop.JniRuntime.JniValueManager Java.Interop.JniRuntime.JniValueManager.ConstructPeer(Java.Interop.IJavaPeerable! peer, ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options) -> void Java.Interop.JniRuntime.JniValueManager.CreateValue(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type? targetType = null) -> object? @@ -899,16 +897,16 @@ virtual Java.Interop.JniRuntime.JniObjectReferenceManager.ReleaseLocalReference( virtual Java.Interop.JniRuntime.JniObjectReferenceManager.WriteGlobalReferenceLine(string! format, params object?[]! args) -> void virtual Java.Interop.JniRuntime.JniObjectReferenceManager.WriteLocalReferenceLine(string! format, params object![]! args) -> void virtual Java.Interop.JniRuntime.JniTypeManager.Dispose(bool disposing) -> void -virtual Java.Interop.JniRuntime.JniTypeManager.GetReplacementMethodInfoCore(string! jniSimpleReference, string! jniMethodName, string! jniMethodSignature) -> Java.Interop.JniRuntime.ReplacementMethodInfo? -virtual Java.Interop.JniRuntime.JniTypeManager.GetReplacementTypeCore(string! jniSimpleReference) -> string? -virtual Java.Interop.JniRuntime.JniTypeManager.GetSimpleReference(System.Type! type) -> string? -virtual Java.Interop.JniRuntime.JniTypeManager.GetSimpleReferences(System.Type! type) -> System.Collections.Generic.IEnumerable! -virtual Java.Interop.JniRuntime.JniTypeManager.GetStaticMethodFallbackTypesCore(string! jniSimple) -> System.Collections.Generic.IReadOnlyList? -virtual Java.Interop.JniRuntime.JniTypeManager.GetTypes(Java.Interop.JniTypeSignature typeSignature) -> System.Collections.Generic.IEnumerable! -virtual Java.Interop.JniRuntime.JniTypeManager.GetTypesForSimpleReference(string! jniSimpleReference) -> System.Collections.Generic.IEnumerable! +abstract Java.Interop.JniRuntime.JniTypeManager.GetReplacementMethodInfoCore(string! jniSimpleReference, string! jniMethodName, string! jniMethodSignature) -> Java.Interop.JniRuntime.ReplacementMethodInfo? +abstract Java.Interop.JniRuntime.JniTypeManager.GetReplacementTypeCore(string! jniSimpleReference) -> string? +abstract Java.Interop.JniRuntime.JniTypeManager.GetSimpleReference(System.Type! type) -> string? +abstract Java.Interop.JniRuntime.JniTypeManager.GetSimpleReferences(System.Type! type) -> System.Collections.Generic.IEnumerable! +abstract Java.Interop.JniRuntime.JniTypeManager.GetStaticMethodFallbackTypesCore(string! jniSimple) -> System.Collections.Generic.IReadOnlyList? +abstract Java.Interop.JniRuntime.JniTypeManager.GetTypes(Java.Interop.JniTypeSignature typeSignature) -> System.Collections.Generic.IEnumerable! +abstract Java.Interop.JniRuntime.JniTypeManager.GetTypesForSimpleReference(string! jniSimpleReference) -> System.Collections.Generic.IEnumerable! virtual Java.Interop.JniRuntime.JniTypeManager.OnSetRuntime(Java.Interop.JniRuntime! runtime) -> void -virtual Java.Interop.JniRuntime.JniTypeManager.RegisterNativeMembers(Java.Interop.JniType! nativeClass, System.Type! type, string? methods) -> void -virtual Java.Interop.JniRuntime.JniTypeManager.RegisterNativeMembers(Java.Interop.JniType! nativeClass, System.Type! type, System.ReadOnlySpan methods) -> void +abstract Java.Interop.JniRuntime.JniTypeManager.RegisterNativeMembers(Java.Interop.JniType! nativeClass, System.Type! type, string? methods) -> void +abstract Java.Interop.JniRuntime.JniTypeManager.RegisterNativeMembers(Java.Interop.JniType! nativeClass, System.Type! type, System.ReadOnlySpan methods) -> void virtual Java.Interop.JniRuntime.JniValueManager.CreatePeer(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions transfer, System.Type? targetType) -> Java.Interop.IJavaPeerable? virtual Java.Interop.JniRuntime.JniValueManager.Dispose(bool disposing) -> void virtual Java.Interop.JniRuntime.JniValueManager.DisposePeer(Java.Interop.IJavaPeerable! value) -> void diff --git a/src/Java.Interop/PublicAPI.Unshipped.txt b/src/Java.Interop/PublicAPI.Unshipped.txt index 359f5560e..df1a7c8ba 100644 --- a/src/Java.Interop/PublicAPI.Unshipped.txt +++ b/src/Java.Interop/PublicAPI.Unshipped.txt @@ -3,11 +3,22 @@ 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? +abstract Java.Interop.JniRuntime.JniTypeManager.GetInvokerTypeCore(System.Type! type) -> System.Type? +abstract Java.Interop.JniRuntime.JniTypeManager.GetReflectionConstructibleTypes(Java.Interop.JniTypeSignature typeSignature) -> System.Collections.Generic.IEnumerable! +abstract Java.Interop.JniRuntime.JniTypeManager.GetTypeForSimpleReference(string! jniSimpleReference) -> System.Type? +abstract Java.Interop.JniRuntime.JniTypeManager.GetTypeSignatureCore(System.Type! type) -> Java.Interop.JniTypeSignature +abstract Java.Interop.JniRuntime.JniTypeManager.GetTypeSignaturesCore(System.Type! type) -> System.Collections.Generic.IEnumerable! 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 +Java.Interop.JniRuntime.DynamicJniTypeManager +Java.Interop.JniRuntime.DynamicJniTypeManager.DynamicJniTypeManager() -> void +Java.Interop.JniRuntime.DynamicJniTypeManager.TryRegisterNativeMembers(Java.Interop.JniType! nativeClass, System.Type! type, string? methods) -> bool +Java.Interop.JniRuntime.DynamicJniTypeManager.TryRegisterNativeMembers(Java.Interop.JniType! nativeClass, System.Type! type, System.ReadOnlySpan methods) -> bool Java.Interop.JniRuntime.JniTypeManager.GetInvokerType(System.Type! type) -> System.Type? +Java.Interop.JniRuntime.JniTypeManager.ReflectionConstructibleType +Java.Interop.JniRuntime.JniTypeManager.ReflectionConstructibleType.ReflectionConstructibleType(System.Type! type) -> void +Java.Interop.JniRuntime.JniTypeManager.ReflectionConstructibleType.Type.get -> 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 @@ -36,3 +47,17 @@ static Java.Interop.JniEnvironment.StaticFields.GetStaticFieldID(Java.Interop.Jn static Java.Interop.JniEnvironment.StaticMethods.GetStaticMethodID(Java.Interop.JniObjectReference type, System.ReadOnlySpan name, System.ReadOnlySpan signature) -> Java.Interop.JniMethodInfo! *REMOVED*abstract Java.Interop.JniRuntime.JniValueManager.ActivatePeer(Java.Interop.IJavaPeerable? self, Java.Interop.JniObjectReference reference, System.Reflection.ConstructorInfo! cinfo, object?[]? argumentValues) -> void virtual Java.Interop.JniRuntime.JniValueManager.ActivatePeer(Java.Interop.JniObjectReference reference, System.Type! type, System.Reflection.ConstructorInfo! cinfo, object?[]? argumentValues) -> void +override Java.Interop.JniRuntime.DynamicJniTypeManager.GetInvokerTypeCore(System.Type! type) -> System.Type? +override Java.Interop.JniRuntime.DynamicJniTypeManager.GetReflectionConstructibleTypes(Java.Interop.JniTypeSignature typeSignature) -> System.Collections.Generic.IEnumerable! +override Java.Interop.JniRuntime.DynamicJniTypeManager.GetReplacementMethodInfoCore(string! jniSimpleReference, string! jniMethodName, string! jniMethodSignature) -> Java.Interop.JniRuntime.ReplacementMethodInfo? +override Java.Interop.JniRuntime.DynamicJniTypeManager.GetReplacementTypeCore(string! jniSimpleReference) -> string? +override Java.Interop.JniRuntime.DynamicJniTypeManager.GetSimpleReference(System.Type! type) -> string? +override Java.Interop.JniRuntime.DynamicJniTypeManager.GetSimpleReferences(System.Type! type) -> System.Collections.Generic.IEnumerable! +override Java.Interop.JniRuntime.DynamicJniTypeManager.GetStaticMethodFallbackTypesCore(string! jniSimple) -> System.Collections.Generic.IReadOnlyList? +override Java.Interop.JniRuntime.DynamicJniTypeManager.GetTypeForSimpleReference(string! jniSimpleReference) -> System.Type? +override Java.Interop.JniRuntime.DynamicJniTypeManager.GetTypeSignatureCore(System.Type! type) -> Java.Interop.JniTypeSignature +override Java.Interop.JniRuntime.DynamicJniTypeManager.GetTypeSignaturesCore(System.Type! type) -> System.Collections.Generic.IEnumerable! +override Java.Interop.JniRuntime.DynamicJniTypeManager.GetTypes(Java.Interop.JniTypeSignature typeSignature) -> System.Collections.Generic.IEnumerable! +override Java.Interop.JniRuntime.DynamicJniTypeManager.GetTypesForSimpleReference(string! jniSimpleReference) -> System.Collections.Generic.IEnumerable! +override Java.Interop.JniRuntime.DynamicJniTypeManager.RegisterNativeMembers(Java.Interop.JniType! nativeClass, System.Type! type, string? methods) -> void +override Java.Interop.JniRuntime.DynamicJniTypeManager.RegisterNativeMembers(Java.Interop.JniType! nativeClass, System.Type! type, System.ReadOnlySpan methods) -> void diff --git a/src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs b/src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs index 128ddb626..98657366a 100644 --- a/src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs +++ b/src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs @@ -9,7 +9,7 @@ namespace Java.Interop { - public class JreTypeManager : JniRuntime.JniTypeManager { + public class JreTypeManager : JniRuntime.DynamicJniTypeManager { IDictionary? typeMappings; @@ -33,6 +33,17 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl yield return target; } + [return: DynamicallyAccessedMembers (JniRuntime.JniTypeManager.MethodsConstructors)] + 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..b3dc71eaf 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Java.Interop; @@ -28,7 +29,7 @@ static partial void CreateJavaVM () } } - class JavaVMFixtureTypeManager : JniRuntime.JniTypeManager { + class JavaVMFixtureTypeManager : JniRuntime.DynamicJniTypeManager { Dictionary TypeMappings = new() { #if !NO_MARSHAL_MEMBER_BUILDER_SUPPORT @@ -50,6 +51,7 @@ class JavaVMFixtureTypeManager : JniRuntime.JniTypeManager { [MyJavaInterfaceImpl.JniTypeName] = typeof (MyJavaInterfaceImpl), }; + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Tests intentionally use the default reflection-based type manager.")] public JavaVMFixtureTypeManager () { } @@ -65,6 +67,13 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl #pragma warning restore CS8600 } + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + 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) @@ -167,4 +176,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..9d7c6eb41 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs @@ -1,6 +1,7 @@ using System; using System.Reflection; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Java.Interop; @@ -26,8 +27,34 @@ public void GetInvokerType () } } - class MyTypeManager : JniRuntime.JniTypeManager { +#if NET + [Test] + public void GetInvokerType_GenericType_Throws () + { + using (var vm = new MyTypeManager ()) { + Assert.Throws (() => vm.GetInvokerType (typeof (IGenericJavaInterface))); + } } - } -} +#endif // NET + + class MyTypeManager : JniRuntime.DynamicJniTypeManager { + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Tests intentionally use the default reflection-based type manager.")] + public MyTypeManager () + { + } + } + + [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/JniRuntimeTest.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs index 3893f2c93..76b4cfeb1 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +using System.Diagnostics.CodeAnalysis; using Java.Interop; @@ -189,7 +190,11 @@ public override void WaitForGCBridgeProcessing () } } - class ProxyTypeManager : JniTypeManager { + class ProxyTypeManager : DynamicJniTypeManager { + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Tests intentionally use the default reflection-based type manager.")] + public ProxyTypeManager () + { + } } } } 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;} } } - From fd4c112f22f69d928ba798c930f26fe9922c0764 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 4 Jun 2026 16:45:08 +0200 Subject: [PATCH 2/2] Remove unnecessary suppression --- src/Java.Interop/Java.Interop/JniValueMarshaler.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Java.Interop/Java.Interop/JniValueMarshaler.cs b/src/Java.Interop/Java.Interop/JniValueMarshaler.cs index f7d911ba5..19015c6ee 100644 --- a/src/Java.Interop/Java.Interop/JniValueMarshaler.cs +++ b/src/Java.Interop/Java.Interop/JniValueMarshaler.cs @@ -169,18 +169,15 @@ public virtual Expression CreateParameterToManagedExpression ( : Expression.Convert (call, targetType); } + [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] Expression CreateSelf (JniValueMarshalerContext context, ParameterExpression sourceValue) { var self = Expression.Variable (GetType (), sourceValue.Name + "_marshaler"); context.LocalVariables.Add (self); - context.CreationStatements.Add (Expression.Assign (self, Expression.New (_GetType ()))); + context.CreationStatements.Add (Expression.Assign (self, Expression.New (GetType ()))); return self; } - [UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = ExpressionRequiresUnreferencedCode)] - [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - Type _GetType () => GetType (); - [RequiresDynamicCode (ExpressionRequiresUnreferencedCode)] [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] public virtual Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue)