diff --git a/external/Java.Interop b/external/Java.Interop index b881d21f51c..cfca8ad36ba 160000 --- a/external/Java.Interop +++ b/external/Java.Interop @@ -1 +1 @@ -Subproject commit b881d21f51cbac6e175de1b2f6c254fe3846aa1d +Subproject commit cfca8ad36ba046f47d64bb8a60fe49e4f4723403 diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs index 08b7b6c66bc..08316ff1787 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs @@ -208,18 +208,15 @@ void ScanAssembly (AssemblyIndex index, Dictionary<(string ManagedName, string A continue; } - // [JniAddNativeMethodRegistrationAttribute] is not supported by the trimmable typemap - // by design (see XA4251). Detect the attribute *before* any per-type filters below - // (array type, no JNI name, etc.) so the diagnostic fires uniformly regardless of - // whether the type would otherwise have ended up in the typemap. - // - // Skip the per-method walk entirely for the overwhelmingly common case where - // the assembly doesn't even reference the attribute type — the per-assembly - // flag was computed cheaply in AssemblyIndex.Build. - if (index.MayUseJniAddNativeMethodRegistrationAttribute && - HasJniAddNativeMethodRegistrationAttribute (typeDef, index)) { - logger?.LogJniAddNativeMethodRegistrationAttributeError (MetadataTypeNameResolver.GetFullName (typeDef, index.Reader)); - } + var fullName = MetadataTypeNameResolver.GetFullName (typeDef, index.Reader); + + // Temporarily allow [JniAddNativeMethodRegistrationAttribute] while we investigate + // which scenarios fail later in the trimmable typemap pipeline. + // if (index.MayUseJniAddNativeMethodRegistrationAttribute && + // !IsBuiltInJniAddNativeMethodRegistrationType (fullName, index) && + // HasJniAddNativeMethodRegistrationAttribute (typeDef, index)) { + // logger?.LogJniAddNativeMethodRegistrationAttributeError (fullName); + // } // Determine the JNI name and whether this is a known Java peer. // Priority: @@ -261,8 +258,6 @@ void ScanAssembly (AssemblyIndex index, Dictionary<(string ManagedName, string A } } - var fullName = MetadataTypeNameResolver.GetFullName (typeDef, index.Reader); - var isInterface = (typeDef.Attributes & TypeAttributes.Interface) != 0; var isAbstract = (typeDef.Attributes & TypeAttributes.Abstract) != 0; var isGenericDefinition = typeDef.GetGenericParameters ().Count > 0; @@ -418,6 +413,12 @@ static bool HasJniAddNativeMethodRegistrationAttribute (TypeDefinition typeDef, return false; } + static bool IsBuiltInJniAddNativeMethodRegistrationType (string fullName, AssemblyIndex index) + { + return string.Equals (index.AssemblyName, "Java.Interop", StringComparison.Ordinal) && + string.Equals (fullName, "Java.Interop.JavaProxyObject", StringComparison.Ordinal); + } + /// /// For each virtual override method on that wasn't already /// collected (no direct [Register]), walks up the base type hierarchy to find a diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs index 918b8377b54..dd0d94a73a9 100644 --- a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs +++ b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs @@ -310,12 +310,14 @@ public override void DeleteWeakGlobalReference (ref JniObjectReference value) } } - class AndroidTypeManager : JniRuntime.JniTypeManager { + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")] + class AndroidTypeManager : JniRuntime.ReflectionJniTypeManager { bool jniAddNativeMethodRegistrationAttributePresent; const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods; const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; + const DynamicallyAccessedMemberTypes MethodsConstructors = MethodsAndPrivateNested | Constructors; public AndroidTypeManager (bool jniAddNativeMethodRegistrationAttributePresent) { @@ -332,13 +334,25 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl yield return t; } + [UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "Temporary suppression until legacy typemap entries carry DAM annotations.")] + [return: DynamicallyAccessedMembers (MethodsConstructors)] + protected override Type? GetTypeForSimpleReference (string jniSimpleReference) + { + var type = base.GetTypeForSimpleReference (jniSimpleReference); + if (type != null) { + return type; + } + + return Java.Interop.TypeManager.GetJavaToManagedType (jniSimpleReference); + } + protected override string? GetSimpleReference (Type type) { string? j = JNIEnv.TypemapManagedToJava (type); if (j != null) { return GetReplacementTypeCore (j) ?? j; } - return null; + return base.GetSimpleReference (type); } protected override IEnumerable GetSimpleReferences (Type type) @@ -347,9 +361,12 @@ protected override IEnumerable GetSimpleReferences (Type type) j = GetReplacementTypeCore (j) ?? j; if (j != null) { - return new[]{j}; + yield return j; + yield break; + } + foreach (var r in base.GetSimpleReferences (type)) { + yield return r; } - return Array.Empty (); } protected override IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimpleReference) @@ -623,7 +640,8 @@ static void SplitMethodLine ( } } - class AndroidValueManager : JniRuntime.JniValueManager { + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")] + class AndroidValueManager : JniRuntime.ReflectionJniValueManager { Dictionary instances = new Dictionary (); diff --git a/src/Mono.Android/Microsoft.Android.Runtime/AndroidReflectionJniValueManager.cs b/src/Mono.Android/Microsoft.Android.Runtime/AndroidReflectionJniValueManager.cs new file mode 100644 index 00000000000..1ead336ccb6 --- /dev/null +++ b/src/Mono.Android/Microsoft.Android.Runtime/AndroidReflectionJniValueManager.cs @@ -0,0 +1,176 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using System.Runtime.CompilerServices; +using Java.Interop; + +namespace Microsoft.Android.Runtime; + +[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")] +abstract class AndroidReflectionJniValueManager : JniRuntime.ReflectionJniValueManager +{ + const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; + + public override IJavaPeerable? CreatePeer ( + ref JniObjectReference reference, + JniObjectReferenceOptions transfer, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType) + { + EnsureNotDisposed (); + + if (!reference.IsValid) { + return null; + } + + targetType = targetType ?? typeof (global::Java.Interop.JavaObject); + targetType = GetPeerType (targetType); + + if (!typeof (IJavaPeerable).IsAssignableFrom (targetType)) { + throw new ArgumentException ($"targetType `{targetType.AssemblyQualifiedName}` must implement IJavaPeerable!", nameof (targetType)); + } + + var targetSig = Runtime.TypeManager.GetTypeSignature (targetType); + if (!targetSig.IsValid || targetSig.SimpleReference == null) { + throw new ArgumentException ($"Could not determine Java type corresponding to `{targetType.AssemblyQualifiedName}`.", nameof (targetType)); + } + + var refClass = JniEnvironment.Types.GetObjectClass (reference); + JniObjectReference targetClass; + try { + targetClass = JniEnvironment.Types.FindClass (targetSig.SimpleReference); + } catch (Exception e) { + JniObjectReference.Dispose (ref refClass); + throw new ArgumentException ($"Could not find Java class `{targetSig.SimpleReference}`.", + nameof (targetType), + e); + } + + if (!JniEnvironment.Types.IsAssignableFrom (refClass, targetClass)) { + JniObjectReference.Dispose (ref refClass); + JniObjectReference.Dispose (ref targetClass); + return null; + } + + JniObjectReference.Dispose (ref targetClass); + + var peer = CreatePeerInstance (ref refClass, targetType, ref reference, transfer); + if (peer == null) { + throw new NotSupportedException (string.Format (CultureInfo.InvariantCulture, "Could not find an appropriate constructable wrapper type for Java type '{0}', targetType='{1}'.", + JniEnvironment.Types.GetJniTypeNameFromInstance (reference), targetType)); + } + peer.SetJniManagedPeerState (peer.JniManagedPeerState | JniManagedPeerStates.Replaceable); + return peer; + } + + [return: DynamicallyAccessedMembers (Constructors)] + static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) + { + if (type == typeof (object)) { + return typeof (global::Java.Interop.JavaObject); + } + if (type == typeof (IJavaPeerable)) { + return typeof (global::Java.Interop.JavaObject); + } + if (type == typeof (Exception)) { + return typeof (JavaException); + } + return type; + } + + IJavaPeerable? CreatePeerInstance ( + ref JniObjectReference klass, + [DynamicallyAccessedMembers (Constructors)] + Type targetType, + ref JniObjectReference reference, + JniObjectReferenceOptions transfer) + { + var jniTypeName = JniEnvironment.Types.GetJniTypeNameFromClass (klass); + + while (jniTypeName != null) { + JniTypeSignature sig; + if (!JniTypeSignature.TryParse (jniTypeName, out sig)) { + return null; + } + + Type? type = GetTypeAssignableTo (sig, targetType); + if (type != null) { + var peer = TryCreatePeerInstance (ref reference, transfer, type); + + if (peer != null) { + JniObjectReference.Dispose (ref klass); + return peer; + } + } + + var super = JniEnvironment.Types.GetSuperclass (klass); + jniTypeName = super.IsValid + ? JniEnvironment.Types.GetJniTypeNameFromClass (super) + : null; + + JniObjectReference.Dispose (ref klass, JniObjectReferenceOptions.CopyAndDispose); + klass = super; + } + JniObjectReference.Dispose (ref klass, JniObjectReferenceOptions.CopyAndDispose); + + return TryCreatePeerInstance (ref reference, transfer, targetType); + + [return: DynamicallyAccessedMembers (Constructors)] + Type? GetTypeAssignableTo (JniTypeSignature sig, Type targetType) + { + foreach (var t in Runtime.TypeManager.GetReflectionConstructibleTypes (sig)) { + if (targetType.IsAssignableFrom (t.Type)) { + return t.Type; + } + } + return null; + } + } + + IJavaPeerable? TryCreatePeerInstance ( + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type type) + { + type = Runtime.TypeManager.GetInvokerType (type) ?? type; + + var self = (IJavaPeerable) RuntimeHelpers.GetUninitializedObject (type); + self.SetJniManagedPeerState (JniManagedPeerStates.Replaceable | JniManagedPeerStates.Activatable); + + var constructed = false; + try { + constructed = TryConstructPeer (self, ref reference, options, type); + } finally { + if (!constructed) { + GC.SuppressFinalize (self); + self = null; + } + } + return self; + } + + static readonly Type ByRefJniObjectReference = typeof (JniObjectReference).MakeByRefType (); + static readonly Type[] JIConstructorSignature = new Type [] { ByRefJniObjectReference, typeof (JniObjectReferenceOptions) }; + + protected virtual bool TryConstructPeer ( + IJavaPeerable self, + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type type) + { + var c = type.GetConstructor (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, JIConstructorSignature, null); + if (c != null) { + var args = new object[] { + reference, + options, + }; + c.Invoke (self, args); + reference = (JniObjectReference) args [0]; + return true; + } + return false; + } +} diff --git a/src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManager.cs b/src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManager.cs index 458ad77202a..cfc32a7900a 100644 --- a/src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManager.cs +++ b/src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManager.cs @@ -18,7 +18,7 @@ namespace Microsoft.Android.Runtime; -class JavaMarshalValueManager : JniRuntime.JniValueManager +class JavaMarshalValueManager : AndroidReflectionJniValueManager { const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; diff --git a/src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.cs b/src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.cs index 454bab0e1bb..bf38800a807 100644 --- a/src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.cs +++ b/src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.cs @@ -7,11 +7,13 @@ namespace Microsoft.Android.Runtime; -class ManagedTypeManager : JniRuntime.JniTypeManager { +[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")] +class ManagedTypeManager : JniRuntime.ReflectionJniTypeManager { 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 | Constructors; public ManagedTypeManager () { @@ -141,6 +143,17 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl } } + [return: DynamicallyAccessedMembers (MethodsConstructors)] + protected override Type? GetTypeForSimpleReference (string jniSimpleReference) + { + var type = base.GetTypeForSimpleReference (jniSimpleReference); + if (type != null) { + return type; + } + + return ManagedTypeMapping.TryGetType (jniSimpleReference, out var target) ? target : null; + } + protected override IEnumerable GetSimpleReferences (Type type) { foreach (var r in base.GetSimpleReferences (type)) { diff --git a/src/Mono.Android/Microsoft.Android.Runtime/SimpleValueManager.cs b/src/Mono.Android/Microsoft.Android.Runtime/SimpleValueManager.cs index bcccf6d6fd1..0546f43d378 100644 --- a/src/Mono.Android/Microsoft.Android.Runtime/SimpleValueManager.cs +++ b/src/Mono.Android/Microsoft.Android.Runtime/SimpleValueManager.cs @@ -16,7 +16,7 @@ namespace Microsoft.Android.Runtime; -class SimpleValueManager : JniRuntime.JniValueManager +class SimpleValueManager : AndroidReflectionJniValueManager { const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; diff --git a/src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.cs b/src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.cs index dea8cb2dcb8..c2514055757 100644 --- a/src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.cs +++ b/src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.cs @@ -14,9 +14,14 @@ namespace Microsoft.Android.Runtime; /// Type manager for the trimmable typemap path. Delegates type lookups /// to . /// -class TrimmableTypeMapTypeManager : JniRuntime.JniTypeManager +[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")] +class TrimmableTypeMapTypeManager : JniRuntime.ReflectionJniTypeManager { const string NoSimpleReference = "\0"; + const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; + const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods; + const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; + const DynamicallyAccessedMemberTypes MethodsConstructors = MethodsAndPrivateNested | Constructors; readonly ConcurrentDictionary _simpleReferenceCache = new (); protected override IEnumerable GetTypesForSimpleReference (string jniSimpleReference) @@ -32,6 +37,21 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl } } + [UnconditionalSuppressMessage ("Trimming", "IL2063", Justification = "Temporary suppression until trimmable typemap type entries carry DAM annotations.")] + [return: DynamicallyAccessedMembers (MethodsConstructors)] + protected override Type? GetTypeForSimpleReference (string jniSimpleReference) + { + var type = base.GetTypeForSimpleReference (jniSimpleReference); + if (type != null) { + return type; + } + + if (TrimmableTypeMap.Instance.TryGetTargetTypes (jniSimpleReference, out var types)) { + return types [0]; + } + return null; + } + protected override string? GetSimpleReference (Type type) { var simpleReference = _simpleReferenceCache.GetOrAdd (type, GetSimpleReferenceUncached); diff --git a/src/Mono.Android/Mono.Android.csproj b/src/Mono.Android/Mono.Android.csproj index 611ef7edf07..f8013325628 100644 --- a/src/Mono.Android/Mono.Android.csproj +++ b/src/Mono.Android/Mono.Android.csproj @@ -10,7 +10,7 @@ Android true ..\..\product.snk - 0618;0809;0108;0114;0465;8609;8610;8614;8617;8613;8764;8765;8766;8767;RS0041 + 0618;0809;0108;0114;0465;8609;8610;8614;8617;8613;8764;8765;8766;8767;RS0041;IL3050 $(WarningsAsErrors);CS2002 true $(DefineConstants);JAVA_INTEROP @@ -363,6 +363,7 @@ + diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets index e98c23cf5e6..2811ab5f6da 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets @@ -24,10 +24,12 @@ <_TypeMapOutputDirectory>$(_TypeMapBaseOutputDir)typemap/ <_TypeMapJavaOutputDirectory>$(_TypeMapBaseOutputDir)typemap/java <_TypeMapAssembliesListFile>$(_TypeMapOutputDirectory)typemap-assemblies.txt - <_PostTrimTypeMapJavaOutputDirectory>$(_TypeMapBaseOutputDir)typemap/linked-java + <_PostTrimTypeMapJavaBaseOutputDir Condition=" '$(_OuterIntermediateOutputPath)' != '' ">$(IntermediateOutputPath) + <_PostTrimTypeMapJavaBaseOutputDir Condition=" '$(_PostTrimTypeMapJavaBaseOutputDir)' == '' ">$(_TypeMapBaseOutputDir) + <_PostTrimTypeMapJavaOutputDirectory>$(_PostTrimTypeMapJavaBaseOutputDir)typemap/linked-java <_TypeMapJavaStubsSourceDirectory Condition=" '$(_TypeMapJavaStubsSourceDirectory)' == '' and '$(_AndroidRuntime)' == 'CoreCLR' and '$(PublishTrimmed)' == 'true' ">$(_PostTrimTypeMapJavaOutputDirectory) <_TypeMapJavaStubsSourceDirectory Condition=" '$(_TypeMapJavaStubsSourceDirectory)' == '' ">$(_TypeMapJavaOutputDirectory) - <_PostTrimTrimmableTypeMapJavaStamp>$(_TypeMapBaseOutputDir)stamp/_GeneratePostTrimTrimmableTypeMapJavaSources.stamp + <_PostTrimTrimmableTypeMapJavaStamp>$(_PostTrimTypeMapJavaBaseOutputDir)stamp/_GeneratePostTrimTrimmableTypeMapJavaSources.stamp <_TrimmableJavaSourceStamp Condition=" '$(_TrimmableJavaSourceStamp)' == '' and '$(_AndroidRuntime)' == 'CoreCLR' and '$(PublishTrimmed)' == 'true' ">$(_PostTrimTrimmableTypeMapJavaStamp) <_TrimmableJavaSourceStamp Condition=" '$(_TrimmableJavaSourceStamp)' == '' ">$(_TypeMapOutputDirectory)$(_TypeMapAssemblyName).dll - - Java.Interop.GenericMarshaler\JniPeerInstanceMethodsExtensions.cs - - -