diff --git a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs index caa5187a7..42010c449 100644 --- a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs @@ -4,11 +4,22 @@ namespace Java.Interop.Samples.NativeAotFromAndroid; -partial class NativeAotTypeManager : JniRuntime.JniTypeManager { - - internal const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods; - internal const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; - internal const DynamicallyAccessedMemberTypes MethodsConstructors = MethodsAndPrivateNested | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; +// This sample derives from the reflection-based JniRuntime.ReflectionJniTypeManager, which is +// annotated [RequiresDynamicCode]/[RequiresUnreferencedCode], so the constructor below suppresses +// the resulting IL2026/IL3050 trim/AOT warnings. +// +// Suppressing here is intentional and good enough: these NativeAOT projects are *samples*, not +// product code. .NET for Android (what we actually ship) does not pair ReflectionJniTypeManager +// with NativeAOT, so it isn't worth the effort to make these samples fully trim/AOT-clean right now. +// The reflection paths were always trim/AOT-unsafe: before dotnet/java-interop#1441 the equivalent +// suppressions lived (buried) inside JniTypeManager itself, justified "NotUsedInAndroid"; #1441 just +// moved that responsibility to callers via [RequiresDynamicCode]/[RequiresUnreferencedCode]. +partial class NativeAotTypeManager : JniRuntime.ReflectionJniTypeManager { + + const DynamicallyAccessedMemberTypes MethodsConstructors = + DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | + DynamicallyAccessedMemberTypes.NonPublicNestedTypes | + DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; Dictionary typeMappings = new () { ["android/app/Activity"] = typeof (Android.App.Activity), @@ -20,136 +31,41 @@ partial class NativeAotTypeManager : JniRuntime.JniTypeManager { ["my/MainActivity"] = typeof (MainActivity), }; - public override void RegisterNativeMembers ( - JniType nativeClass, - [DynamicallyAccessedMembers (MethodsAndPrivateNested)] - Type type, - ReadOnlySpan methods) + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Sample only (see class comment): this assembly is rooted via TrimmerRootAssembly and the members reflected over during registration are preserved by the [DynamicallyAccessedMembers] annotations on the RegisterNativeMembers(Type) -> FindAndCallRegisterMethod path, so trimming does not remove what reflection needs.")] + [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Sample only (see class comment): built-in member registration calls CreateDelegate on compile-time-known static methods (no MakeGenericType / expression compilation), so no runtime code generation is required.")] + public NativeAotTypeManager () { - if (TryRegisterBuiltInNativeMembers (nativeClass, nativeClass.Name, methods)) - return; - if (!methods.IsEmpty) - throw new NotSupportedException ($"Could not register native members for type '{type.FullName}'."); } - [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] - public override void RegisterNativeMembers ( - JniType nativeClass, - [DynamicallyAccessedMembers (MethodsAndPrivateNested)] - Type type, - string? methods) + // GetType() dispatches through GetTypeForSimpleReference (singular), so the sample's own type + // map has to be applied here; the base ReflectionJniTypeManager only knows the built-in types. + [return: DynamicallyAccessedMembers (MethodsConstructors)] + protected override Type? GetTypeForSimpleReference (string jniSimpleReference) { - RegisterNativeMembers (nativeClass, type, methods.AsSpan ()); + if (typeMappings.TryGetValue (jniSimpleReference, out var target)) + return target; + return base.GetTypeForSimpleReference (jniSimpleReference); } protected override IEnumerable GetTypesForSimpleReference (string jniSimpleReference) { - var target = GetTypeForSimpleReference (jniSimpleReference); - if (target != null) + if (typeMappings.TryGetValue (jniSimpleReference, out var target)) yield return target; - } - - protected override string? GetSimpleReference (Type type) - { - return GetSimpleReferences (type).FirstOrDefault (); - } - - [return: DynamicallyAccessedMembers (MethodsConstructors)] - protected override Type? GetTypeForSimpleReference (string jniSimpleReference) - { - return jniSimpleReference switch { - "V" => typeof (void), - "Z" => typeof (bool), - "java/lang/Boolean" => typeof (bool?), - "B" => typeof (sbyte), - "java/lang/Byte" => typeof (sbyte?), - "C" => typeof (char), - "java/lang/Character" => typeof (char?), - "S" => typeof (short), - "java/lang/Short" => typeof (short?), - "I" => typeof (int), - "java/lang/Integer" => typeof (int?), - "J" => typeof (long), - "java/lang/Long" => typeof (long?), - "F" => typeof (float), - "java/lang/Float" => typeof (float?), - "D" => typeof (double), - "java/lang/Double" => typeof (double?), - "android/app/Activity" => typeof (Android.App.Activity), - "android/content/Context" => typeof (Android.Content.Context), - "android/content/ContextWrapper" => typeof (Android.Content.ContextWrapper), - "android/os/BaseBundle" => typeof (Android.OS.BaseBundle), - "android/os/Bundle" => typeof (Android.OS.Bundle), - "android/view/ContextThemeWrapper" => typeof (Android.View.ContextThemeWrapper), - "my/MainActivity" => typeof (MainActivity), - _ => null, - }; + foreach (var t in base.GetTypesForSimpleReference (jniSimpleReference)) + yield return t; } protected override IEnumerable GetSimpleReferences (Type type) { - return CreateSimpleReferencesEnumerator (type); + return base.GetSimpleReferences (type) + .Concat (CreateSimpleReferencesEnumerator (type)); } IEnumerable CreateSimpleReferencesEnumerator (Type type) { - if (typeMappings == null) - yield break; foreach (var e in typeMappings) { if (e.Value == 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) - { - if (!typeSignature.IsValid || typeSignature.ArrayRank != 0 || typeSignature.SimpleReference == null) - yield break; - var target = GetTypeForSimpleReference (typeSignature.SimpleReference); - if (target != null) - yield return new JniRuntime.JniTypeManager.ReflectionConstructibleType (target); - } - - protected override JniTypeSignature GetTypeSignatureCore (Type type) - { - var simpleReference = GetSimpleReferences (type).FirstOrDefault (); - 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; - } - - [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] - protected override Type? GetInvokerTypeCore ( - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] - Type type) - { - return null; - } - - protected override IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimpleReference) - { - return null; - } - - protected override string? GetReplacementTypeCore (string jniSimpleReference) - { - return null; - } - - protected override JniRuntime.ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSourceType, string jniMethodName, string jniMethodSignature) - { - return null; - } } diff --git a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs index e1a3150b3..3dbea075f 100644 --- a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs @@ -1,148 +1,49 @@ -using Java.Interop; using System.Diagnostics.CodeAnalysis; -namespace Hello_NativeAOTFromJNI; - -class NativeAotTypeManager : JniRuntime.JniTypeManager { - internal const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods; - internal const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; - internal const DynamicallyAccessedMemberTypes MethodsConstructors = MethodsAndPrivateNested | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; +using Java.Interop; - protected override IEnumerable GetTypesForSimpleReference (string jniSimpleReference) - { - var target = GetTypeForSimpleReference (jniSimpleReference); - if (target != null) - yield return target; - } +namespace Hello_NativeAOTFromJNI; +// This sample derives from the reflection-based JniRuntime.ReflectionJniTypeManager, which is +// annotated [RequiresDynamicCode]/[RequiresUnreferencedCode], so the constructor below suppresses +// the resulting IL2026/IL3050 trim/AOT warnings. +// +// Suppressing here is intentional and good enough: these NativeAOT projects are *samples*, not +// product code. .NET for Android (what we actually ship) does not pair ReflectionJniTypeManager +// with NativeAOT, so it isn't worth the effort to make these samples fully trim/AOT-clean right now. +// The reflection paths were always trim/AOT-unsafe: before dotnet/java-interop#1441 the equivalent +// suppressions lived (buried) inside JniTypeManager itself, justified "NotUsedInAndroid"; #1441 just +// moved that responsibility to callers via [RequiresDynamicCode]/[RequiresUnreferencedCode]. +class NativeAotTypeManager : JniRuntime.ReflectionJniTypeManager { + + const DynamicallyAccessedMemberTypes MethodsConstructors = + DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | + DynamicallyAccessedMemberTypes.NonPublicNestedTypes | + DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; + + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Sample only (see class comment): this assembly is rooted via TrimmerRootAssembly and the members reflected over during registration are preserved by the [DynamicallyAccessedMembers] annotations on the RegisterNativeMembers(Type) -> FindAndCallRegisterMethod path, so trimming does not remove what reflection needs.")] + [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Sample only (see class comment): built-in member registration calls CreateDelegate on compile-time-known static methods (no MakeGenericType / expression compilation), so no runtime code generation is required.")] + public NativeAotTypeManager () + { + } + + // The base ReflectionJniTypeManager resolves built-in types (primitives, java/lang/String, + // JavaProxyObject, ...) and handles registration and the reverse Type->JNI mapping (via the + // [JniTypeSignature] attribute) for us. We only need to teach it about this sample's own + // managed types. [return: DynamicallyAccessedMembers (MethodsConstructors)] protected override Type? GetTypeForSimpleReference (string jniSimpleReference) { - return jniSimpleReference switch { - "V" => typeof (void), - "Z" => typeof (bool), - "java/lang/Boolean" => typeof (bool?), - "B" => typeof (sbyte), - "java/lang/Byte" => typeof (sbyte?), - "C" => typeof (char), - "java/lang/Character" => typeof (char?), - "S" => typeof (short), - "java/lang/Short" => typeof (short?), - "I" => typeof (int), - "java/lang/Integer" => typeof (int?), - "J" => typeof (long), - "java/lang/Long" => typeof (long?), - "F" => typeof (float), - "java/lang/Float" => typeof (float?), - "D" => typeof (double), - "java/lang/Double" => typeof (double?), - Example.ManagedType.JniTypeName => typeof (Example.ManagedType), - "java/lang/Object" => typeof (Java.Lang.Object), - "java/lang/String" => typeof (Java.Lang.String), - _ => null, - }; - } - - 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) - { - if (!typeSignature.IsValid || typeSignature.ArrayRank != 0 || typeSignature.SimpleReference == null) - yield break; - var target = GetTypeForSimpleReference (typeSignature.SimpleReference); - if (target != null) - yield return new JniRuntime.JniTypeManager.ReflectionConstructibleType (target); - } - - protected override IEnumerable GetSimpleReferences (Type type) - { - return CreateSimpleReferencesEnumerator (type); - } - - IEnumerable CreateSimpleReferencesEnumerator (Type type) - { - if (type == typeof (Example.ManagedType)) - yield return Example.ManagedType.JniTypeName; - else if (type == typeof (Java.Lang.Object)) - yield return "java/lang/Object"; - else if (type == typeof (Java.Lang.String)) - yield return "java/lang/String"; - } - - protected override string? GetSimpleReference (Type type) - { - return GetSimpleReferences (type).FirstOrDefault (); + if (jniSimpleReference == Example.ManagedType.JniTypeName) + return typeof (Example.ManagedType); + return base.GetTypeForSimpleReference (jniSimpleReference); } - 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; - } - - [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] - protected override Type? GetInvokerTypeCore ( - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] - Type type) - { - return null; - } - - protected override IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimpleReference) - { - return null; - } - - protected override string? GetReplacementTypeCore (string jniSimpleReference) - { - return null; - } - - protected override JniRuntime.ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSourceType, string jniMethodName, string jniMethodSignature) - { - return null; - } - - public override void RegisterNativeMembers ( - JniType nativeClass, - [DynamicallyAccessedMembers (MethodsAndPrivateNested)] - Type type, - ReadOnlySpan methods) - { - if (TryRegisterBuiltInNativeMembers (nativeClass, nativeClass.Name, methods)) - return; - - 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 ()); - } - - [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] - public override void RegisterNativeMembers ( - JniType nativeClass, - [DynamicallyAccessedMembers (MethodsAndPrivateNested)] - Type type, - string? methods) + protected override IEnumerable GetTypesForSimpleReference (string jniSimpleReference) { - RegisterNativeMembers (nativeClass, type, methods.AsSpan ()); + if (jniSimpleReference == Example.ManagedType.JniTypeName) + yield return typeof (Example.ManagedType); + foreach (var t in base.GetTypesForSimpleReference (jniSimpleReference)) + yield return t; } } diff --git a/src/Java.Interop/Java.Interop/JavaProxyObject.cs b/src/Java.Interop/Java.Interop/JavaProxyObject.cs index 0050cf291..909629b18 100644 --- a/src/Java.Interop/Java.Interop/JavaProxyObject.cs +++ b/src/Java.Interop/Java.Interop/JavaProxyObject.cs @@ -16,7 +16,7 @@ sealed class JavaProxyObject : JavaObject, IEquatable static readonly ConditionalWeakTable CachedValues = new ConditionalWeakTable (); [JniAddNativeMethodRegistrationAttribute] - internal static void RegisterNativeMembers (JniNativeMethodRegistrationArguments args) + static void RegisterNativeMembers (JniNativeMethodRegistrationArguments args) { args.Registrations.Add (new JniNativeMethodRegistration ("equals", "(Ljava/lang/Object;)Z", new EqualsMarshalMethod (Equals))); args.Registrations.Add (new JniNativeMethodRegistration ("hashCode", "()I", new GetHashCodeMarshalMethod (GetHashCode))); diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs index 379b70847..ce7bcc563 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs @@ -253,22 +253,6 @@ static JniTypeSignature GetBuiltInTypeSignature (Type type) }; } - protected static bool TryRegisterBuiltInNativeMembers ( - JniType nativeClass, - string jniSimpleReference, - ReadOnlySpan methods) - { - if (jniSimpleReference == JavaProxyObject.JniTypeName) { - var registrations = new List (); - JavaProxyObject.RegisterNativeMembers (new JniNativeMethodRegistrationArguments (registrations, null)); - if (registrations.Count > 0) - nativeClass.RegisterNativeMethods (registrations.ToArray ()); - return true; - } - - return jniSimpleReference == JavaProxyThrowable.JniTypeName && methods.IsEmpty; - } - /// [return: DynamicallyAccessedMembers (Constructors)] public Type? GetInvokerType ( diff --git a/src/Java.Interop/PublicAPI.Unshipped.txt b/src/Java.Interop/PublicAPI.Unshipped.txt index 2051fd1e8..15c6329c0 100644 --- a/src/Java.Interop/PublicAPI.Unshipped.txt +++ b/src/Java.Interop/PublicAPI.Unshipped.txt @@ -8,7 +8,6 @@ virtual Java.Interop.JniRuntime.JniTypeManager.GetReflectionConstructibleTypes(J virtual Java.Interop.JniRuntime.JniTypeManager.GetTypeForSimpleReference(string! jniSimpleReference) -> System.Type? virtual Java.Interop.JniRuntime.JniTypeManager.GetTypeSignatureCore(System.Type! type) -> Java.Interop.JniTypeSignature virtual Java.Interop.JniRuntime.JniTypeManager.GetTypeSignaturesCore(System.Type! type) -> System.Collections.Generic.IEnumerable! -static Java.Interop.JniRuntime.JniTypeManager.TryRegisterBuiltInNativeMembers(Java.Interop.JniType! nativeClass, string! jniSimpleReference, System.ReadOnlySpan methods) -> 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.ReflectionJniTypeManager