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
-
-
-