From 58f0dbdc00918e39b18a90097d30856dd3c51234 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 4 Jun 2026 16:01:19 +0200 Subject: [PATCH 01/28] 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 02/28] 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) From f2246b363b4ddf612655ddea44d84cfd229b5ae6 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 8 Jun 2026 10:48:14 +0200 Subject: [PATCH 03/28] Move DynamicJniTypeManager to its own separate file --- .../JniRuntime.DynamicJniTypeManager.cs | 521 ++++++++++++++++++ .../Java.Interop/JniRuntime.JniTypeManager.cs | 508 ----------------- 2 files changed, 521 insertions(+), 508 deletions(-) create mode 100644 src/Java.Interop/Java.Interop/JniRuntime.DynamicJniTypeManager.cs diff --git a/src/Java.Interop/Java.Interop/JniRuntime.DynamicJniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.DynamicJniTypeManager.cs new file mode 100644 index 000000000..c3a20953d --- /dev/null +++ b/src/Java.Interop/Java.Interop/JniRuntime.DynamicJniTypeManager.cs @@ -0,0 +1,521 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; +using System.Threading; + +namespace Java.Interop { + + public partial class JniRuntime { + + [RequiresDynamicCode ("This JniTypeManager implementation is not compatible with Native AOT. Use a different JniTypeManager implementation that supports Native AOT.")] + [RequiresUnreferencedCode ("This JniTypeManager implementation is not compatible with Native AOT. Use a different JniTypeManager implementation that supports Native AOT.")] + public partial class DynamicJniTypeManager : JniTypeManager { + + protected override JniTypeSignature GetTypeSignatureCore (Type type) + { + type = GetUnderlyingType (type, out int rank); + + JniTypeSignature signature = JniTypeSignature.Empty; + if (GetBuiltInTypeSignature (type, ref signature)) + return signature.AddArrayRank (rank); + if (GetBuiltInTypeArraySignature (type, ref signature)) + return signature.AddArrayRank (rank); + + var isGeneric = type.IsGenericType; + var genericDef = isGeneric ? type.GetGenericTypeDefinition () : type; + if (isGeneric) { + if (genericDef == typeof (JavaArray<>) || genericDef == typeof (JavaObjectArray<>)) { + var r = GetTypeSignature (type.GenericTypeArguments [0]); + return r.AddArrayRank (rank + 1); + } + + var genericSimpleRef = GetSimpleReference (genericDef); + if (genericSimpleRef != null) + return new JniTypeSignature (genericSimpleRef, rank, false); + } + + var simpleRef = GetSimpleReference (type); + if (simpleRef != null) + return new JniTypeSignature (simpleRef, rank, false); + + return default; + } + + protected override IEnumerable GetTypeSignaturesCore (Type type) + { + type = GetUnderlyingType (type, out int rank); + + var signature = JniTypeSignature.Empty; + if (GetBuiltInTypeSignature (type, ref signature)) + yield return signature.AddArrayRank (rank); + if (GetBuiltInTypeArraySignature (type, ref signature)) + yield return signature.AddArrayRank (rank); + + var isGeneric = type.IsGenericType; + var genericDef = isGeneric ? type.GetGenericTypeDefinition () : type; + if (isGeneric) { + if (genericDef == typeof (JavaArray<>) || genericDef == typeof (JavaObjectArray<>)) { + var r = GetTypeSignature (type.GenericTypeArguments [0]); + yield return r.AddArrayRank (rank + 1); + } + + foreach (var genericSimpleRef in GetSimpleReferences (genericDef)) { + if (genericSimpleRef == null) + continue; + yield return new JniTypeSignature (genericSimpleRef, rank, false); + } + } + + foreach (var simpleRef in GetSimpleReferences (type)) { + if (simpleRef == null) + continue; + yield return new JniTypeSignature (simpleRef, rank, false); + } + } + + static Type GetUnderlyingType (Type type, out int rank) + { + rank = 0; + var originalType = type; + while (type.IsArray) { + if (type.IsArray && type.GetArrayRank () > 1) + throw new ArgumentException ("Multidimensional array '" + originalType.FullName + "' is not supported.", nameof (type)); + rank++; + type = type.GetElementType ()!; + } + + if (type.IsEnum) + type = Enum.GetUnderlyingType (type); + + return type; + } + + // `type` will NOT be an array type. + protected override string? GetSimpleReference (Type type) + { + return GetSimpleReferences (type).FirstOrDefault (); + } + + // `type` will NOT be an array type. + protected override IEnumerable GetSimpleReferences (Type type) + { + AssertValid (); + + if (type == null) + throw new ArgumentNullException (nameof (type)); + if (type.IsArray) + throw new ArgumentException ("Array type '" + type.FullName + "' is not supported.", nameof (type)); + + var name = type.GetCustomAttribute (inherit: false); + if (name != null) { + var altRef = GetReplacementType (name.SimpleReference); + if (altRef != null) { + yield return altRef; + } else { + yield return name.SimpleReference; + } + } + + yield break; + } + + static readonly string[] EmptyStringArray = Array.Empty (); + static readonly Type[] EmptyTypeArray = Array.Empty (); + + readonly struct KnownArrayTypesInfo + { + public readonly Dictionary ArrayTypes; + public readonly Dictionary JavaObjectArrayTypes; + + public KnownArrayTypesInfo (Dictionary arrayTypes, Dictionary javaObjectArrayTypes) + { + ArrayTypes = arrayTypes; + JavaObjectArrayTypes = javaObjectArrayTypes; + } + } + + static readonly Lazy KnownArrayTypes = new Lazy (InitKnownArrayTypes); + + static KnownArrayTypesInfo InitKnownArrayTypes () + { + var arrayTypes = new Dictionary (); + var javaObjectArrayTypes = new Dictionary (); + + AddKnownArrayTypes (arrayTypes, javaObjectArrayTypes); + + AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); + AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); + AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); + AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); + AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); + AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); + AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); + AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); + + return new KnownArrayTypesInfo (arrayTypes, javaObjectArrayTypes); + } + + static void AddKnownPrimitiveArrayTypes< + [DynamicallyAccessedMembers (Constructors)] + T, + [DynamicallyAccessedMembers (Constructors)] + TArray> (Dictionary arrayTypes, Dictionary javaObjectArrayTypes) + { + AddKnownArrayTypes (arrayTypes, javaObjectArrayTypes); + AddKnownArrayTypes> (arrayTypes, javaObjectArrayTypes); + AddKnownArrayTypes> (arrayTypes, javaObjectArrayTypes); + AddKnownArrayTypes (arrayTypes, javaObjectArrayTypes); + } + + static void AddKnownArrayTypes< + [DynamicallyAccessedMembers (Constructors)] + T> (Dictionary arrayTypes, Dictionary javaObjectArrayTypes) + { + arrayTypes [typeof (T)] = typeof (T[]); + arrayTypes [typeof (T[])] = typeof (T[][]); + arrayTypes [typeof (T[][])] = typeof (T[][][]); + javaObjectArrayTypes [typeof (T)] = typeof (JavaObjectArray); + javaObjectArrayTypes [typeof (JavaObjectArray)] = typeof (JavaObjectArray>); + } + + static bool TryMakeArrayType (Type type, out Type? arrayType) => + KnownArrayTypes.Value.ArrayTypes.TryGetValue (type, out arrayType); + + static bool TryMakeJavaObjectArrayType (Type type, out Type? arrayType) => + KnownArrayTypes.Value.JavaObjectArrayTypes.TryGetValue (type, out arrayType); + + 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."); + + [return: DynamicallyAccessedMembers (MethodsConstructors)] + protected override Type? GetTypeForSimpleReference (string jniSimpleReference) + { + AssertValid (); + AssertSimpleReference (jniSimpleReference); + + return jniSimpleReference switch { + "java/lang/String" => TypeOf (), + "V" => TypeOfVoid (), + "Z" => TypeOf (), + "java/lang/Boolean" => TypeOf (), + "B" => TypeOf (), + "java/lang/Byte" => TypeOf (), + "C" => TypeOf (), + "java/lang/Character" => TypeOf (), + "S" => TypeOf (), + "java/lang/Short" => TypeOf (), + "I" => TypeOf (), + "java/lang/Integer" => TypeOf (), + "J" => TypeOf (), + "java/lang/Long" => TypeOf (), + "F" => TypeOf (), + "java/lang/Float" => TypeOf (), + "D" => TypeOf (), + "java/lang/Double" => TypeOf (), + _ => null, + }; + } + + [return: DynamicallyAccessedMembers (MethodsConstructors)] + static Type TypeOf< + [DynamicallyAccessedMembers (MethodsConstructors)] + T> () => typeof (T); + + [return: DynamicallyAccessedMembers (MethodsConstructors)] + static Type TypeOfVoid () => typeof (void); + + public override IEnumerable GetTypes (JniTypeSignature typeSignature) + { + AssertValid (); + + if (typeSignature.SimpleReference == null) + return EmptyTypeArray; + 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"))) { + if (typeSignature.ArrayRank == 0) { + yield return type; + continue; + } + + if (typeSignature.IsKeyword) { + foreach (var t in GetPrimitiveArrayTypesForSimpleReference (typeSignature, type)) { + yield return t; + } + continue; + } + + if (typeSignature.ArrayRank > 0) { + var rank = typeSignature.ArrayRank; + var arrayType = type; + while (rank-- > 0) { + arrayType = TryMakeJavaObjectArrayType (arrayType, out var nextArrayType) + ? nextArrayType ?? throw new InvalidOperationException ("Should not be reached") + : GetUnsupportedJavaObjectArrayType (arrayType); + } + yield return arrayType; + } + + if (typeSignature.ArrayRank > 0) { + var rank = typeSignature.ArrayRank; + var arrayType = type; + while (rank-- > 0) { + arrayType = TryMakeArrayType (arrayType, out var nextArrayType) + ? nextArrayType ?? throw new InvalidOperationException ("Should not be reached") + : GetUnsupportedArrayType (arrayType); + } + yield return arrayType; + } + } + } + + IEnumerable GetPrimitiveArrayTypesForSimpleReference (JniTypeSignature typeSignature, Type type) + { + int index = -1; + for (int i = 0; i < JniPrimitiveArrayTypes.Length; ++i) { + if (JniPrimitiveArrayTypes [i].PrimitiveType == type) { + index = i; + break; + } + } + if (index == -1) { + throw new InvalidOperationException ($"Should not be reached; Could not find JniPrimitiveArrayInfo for {type}"); + } + foreach (var t in JniPrimitiveArrayTypes [index].ArrayTypes) { + var rank = typeSignature.ArrayRank-1; + var arrayType = t; + while (rank-- > 0) { + 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 = TryMakeArrayType (arrayType, out var nextArrayType) + ? nextArrayType ?? throw new InvalidOperationException ("Should not be reached") + : GetUnsupportedArrayType (arrayType); + } + yield return arrayType; + } + } + + protected override IEnumerable GetTypesForSimpleReference (string jniSimpleReference) + { + AssertValid (); + AssertSimpleReference (jniSimpleReference); + + // Not sure why CS8604 is reported on following line when we check against null ~9 lines above... + return CreateGetTypesForSimpleReferenceEnumerator (jniSimpleReference!); + } + + IEnumerable CreateGetTypesForSimpleReferenceEnumerator (string jniSimpleReference) + { + if (JniBuiltinSimpleReferenceToType.Value.TryGetValue (jniSimpleReference, out var ret)) { + yield return ret; + } + if (RuntimeFeature.ManagedPeerNativeRegistration && jniSimpleReference == ManagedPeer.JniTypeName) { + yield return typeof (ManagedPeer); + } + yield break; + } + + [return: DynamicallyAccessedMembers (Constructors)] + protected override Type? GetInvokerTypeCore ( + [DynamicallyAccessedMembers (Constructors)] + Type type) + { + var signature = type.GetCustomAttribute (); + if (signature == null || signature.InvokerType == null) { + return null; + } + + Type[] arguments = type.GetGenericArguments (); + if (arguments.Length == 0) + return signature.InvokerType; + +#if NET + throw new NotSupportedException ($"Generic invoker type construction for `{type}` is not supported."); +#else // NET + return signature.InvokerType.MakeGenericType (arguments); +#endif // NET + } + +#if NET + + protected override IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimple) => null; + + protected override string? GetReplacementTypeCore (string jniSimpleReference) => null; + + protected override ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSimpleReference, string jniMethodName, string jniMethodSignature) => null; + + public override void RegisterNativeMembers ( + JniType nativeClass, + [DynamicallyAccessedMembers (MethodsAndPrivateNested)] + Type type, + ReadOnlySpan methods) + { + TryRegisterNativeMembers (nativeClass, type, methods); + } + + protected bool TryRegisterNativeMembers ( + JniType nativeClass, + [DynamicallyAccessedMembers (MethodsAndPrivateNested)] + Type type, + ReadOnlySpan methods) + { + AssertValid (); + +#pragma warning disable CS1717 + methods = methods; +#pragma warning restore CS1717 + + return TryLoadJniMarshalMethods (nativeClass, type, null) || TryRegisterNativeMembers (nativeClass, type, null, null); + } +#endif // NET + +#if NET + [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] +#endif // NET + public override void RegisterNativeMembers ( + JniType nativeClass, + [DynamicallyAccessedMembers (MethodsAndPrivateNested)] + Type type, + string? methods) + { + TryRegisterNativeMembers (nativeClass, type, methods); + } + +#if NET + [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] +#endif // NET + protected bool TryRegisterNativeMembers ( + JniType nativeClass, + [DynamicallyAccessedMembers (MethodsAndPrivateNested)] + Type type, + string? methods) + { + AssertValid (); + + return TryLoadJniMarshalMethods (nativeClass, type, methods) || TryRegisterNativeMembers (nativeClass, type, methods, null); + } + + static Type [] registerMethodParameters = new Type [] { typeof (JniNativeMethodRegistrationArguments) }; + + bool TryLoadJniMarshalMethods ( + JniType nativeClass, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + Type type, + string? methods) + { + var marshalType = type?.GetNestedType ("__<$>_jni_marshal_methods", BindingFlags.NonPublic); + if (marshalType == null) { + return false; + } + + var registerMethod = marshalType.GetMethod ( + name: "__RegisterNativeMembers", + bindingAttr: BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, + binder: null, + callConvention: default, + types: registerMethodParameters, + modifiers: null); + return TryRegisterNativeMembers (nativeClass, marshalType, methods, registerMethod); + } + + static List sharedRegistrations = new List (); + + bool TryRegisterNativeMembers ( + JniType nativeClass, + [DynamicallyAccessedMembers (Methods)] + Type marshalType, + string? methods, + MethodInfo? registerMethod) + { + bool lockTaken = false; + bool rv = false; + + try { + Monitor.TryEnter (sharedRegistrations, ref lockTaken); + List registrations; + if (lockTaken) { + sharedRegistrations.Clear (); + registrations = sharedRegistrations; + } else { + registrations = new List (); + } + JniNativeMethodRegistrationArguments arguments = new JniNativeMethodRegistrationArguments (registrations, methods); + if (registerMethod != null) { + registerMethod.Invoke (null, new object [] { arguments }); + rv = true; + } else + rv = FindAndCallRegisterMethod (marshalType, arguments); + + if (registrations.Count > 0) + nativeClass.RegisterNativeMethods (registrations.ToArray ()); + } finally { + if (lockTaken) { + Monitor.Exit (sharedRegistrations); + } + } + + return rv; + } + + bool FindAndCallRegisterMethod ( + [DynamicallyAccessedMembers (Methods)] + Type marshalType, + JniNativeMethodRegistrationArguments arguments) + { + if (!Runtime.JniAddNativeMethodRegistrationAttributePresent) + return false; + + bool found = false; + + foreach (var methodInfo in marshalType.GetRuntimeMethods ()) { + if (methodInfo.GetCustomAttribute (typeof (JniAddNativeMethodRegistrationAttribute)) == null) { + continue; + } + + var declaringTypeName = methodInfo.DeclaringType?.FullName ?? ""; + + if ((methodInfo.Attributes & MethodAttributes.Static) != MethodAttributes.Static) { + throw new InvalidOperationException ($"The method `{declaringTypeName}.{methodInfo}` marked with [{nameof (JniAddNativeMethodRegistrationAttribute)}] must be static!"); + } + + var register = (Action)methodInfo.CreateDelegate (typeof (Action)); + register (arguments); + + found = true; + } + + return found; + } + + } + } +} diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs index 4896211ce..e541947bf 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs @@ -269,513 +269,5 @@ public abstract void RegisterNativeMembers ( 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; - if (GetBuiltInTypeSignature (type, ref signature)) - return signature.AddArrayRank (rank); - if (GetBuiltInTypeArraySignature (type, ref signature)) - return signature.AddArrayRank (rank); - - var isGeneric = type.IsGenericType; - var genericDef = isGeneric ? type.GetGenericTypeDefinition () : type; - if (isGeneric) { - if (genericDef == typeof (JavaArray<>) || genericDef == typeof (JavaObjectArray<>)) { - var r = GetTypeSignature (type.GenericTypeArguments [0]); - return r.AddArrayRank (rank + 1); - } - - var genericSimpleRef = GetSimpleReference (genericDef); - if (genericSimpleRef != null) - return new JniTypeSignature (genericSimpleRef, rank, false); - } - - var simpleRef = GetSimpleReference (type); - if (simpleRef != null) - return new JniTypeSignature (simpleRef, rank, false); - - return default; - } - - protected override IEnumerable GetTypeSignaturesCore (Type type) - { - type = GetUnderlyingType (type, out int rank); - - var signature = JniTypeSignature.Empty; - if (GetBuiltInTypeSignature (type, ref signature)) - yield return signature.AddArrayRank (rank); - if (GetBuiltInTypeArraySignature (type, ref signature)) - yield return signature.AddArrayRank (rank); - - var isGeneric = type.IsGenericType; - var genericDef = isGeneric ? type.GetGenericTypeDefinition () : type; - if (isGeneric) { - if (genericDef == typeof (JavaArray<>) || genericDef == typeof (JavaObjectArray<>)) { - var r = GetTypeSignature (type.GenericTypeArguments [0]); - yield return r.AddArrayRank (rank + 1); - } - - foreach (var genericSimpleRef in GetSimpleReferences (genericDef)) { - if (genericSimpleRef == null) - continue; - yield return new JniTypeSignature (genericSimpleRef, rank, false); - } - } - - foreach (var simpleRef in GetSimpleReferences (type)) { - if (simpleRef == null) - continue; - yield return new JniTypeSignature (simpleRef, rank, false); - } - } - - static Type GetUnderlyingType (Type type, out int rank) - { - rank = 0; - var originalType = type; - while (type.IsArray) { - if (type.IsArray && type.GetArrayRank () > 1) - throw new ArgumentException ("Multidimensional array '" + originalType.FullName + "' is not supported.", nameof (type)); - rank++; - type = type.GetElementType ()!; - } - - if (type.IsEnum) - type = Enum.GetUnderlyingType (type); - - return type; - } - - // `type` will NOT be an array type. - protected override string? GetSimpleReference (Type type) - { - return GetSimpleReferences (type).FirstOrDefault (); - } - - // `type` will NOT be an array type. - protected override IEnumerable GetSimpleReferences (Type type) - { - AssertValid (); - - if (type == null) - throw new ArgumentNullException (nameof (type)); - if (type.IsArray) - throw new ArgumentException ("Array type '" + type.FullName + "' is not supported.", nameof (type)); - - var name = type.GetCustomAttribute (inherit: false); - if (name != null) { - var altRef = GetReplacementType (name.SimpleReference); - if (altRef != null) { - yield return altRef; - } else { - yield return name.SimpleReference; - } - } - - yield break; - } - - static readonly string[] EmptyStringArray = Array.Empty (); - static readonly Type[] EmptyTypeArray = Array.Empty (); - - readonly struct KnownArrayTypesInfo - { - public readonly Dictionary ArrayTypes; - public readonly Dictionary JavaObjectArrayTypes; - - public KnownArrayTypesInfo (Dictionary arrayTypes, Dictionary javaObjectArrayTypes) - { - ArrayTypes = arrayTypes; - JavaObjectArrayTypes = javaObjectArrayTypes; - } - } - - static readonly Lazy KnownArrayTypes = new Lazy (InitKnownArrayTypes); - - static KnownArrayTypesInfo InitKnownArrayTypes () - { - var arrayTypes = new Dictionary (); - var javaObjectArrayTypes = new Dictionary (); - - AddKnownArrayTypes (arrayTypes, javaObjectArrayTypes); - - AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); - AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); - AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); - AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); - AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); - AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); - AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); - AddKnownPrimitiveArrayTypes (arrayTypes, javaObjectArrayTypes); - - return new KnownArrayTypesInfo (arrayTypes, javaObjectArrayTypes); - } - - static void AddKnownPrimitiveArrayTypes< - [DynamicallyAccessedMembers (Constructors)] - T, - [DynamicallyAccessedMembers (Constructors)] - TArray> (Dictionary arrayTypes, Dictionary javaObjectArrayTypes) - { - AddKnownArrayTypes (arrayTypes, javaObjectArrayTypes); - AddKnownArrayTypes> (arrayTypes, javaObjectArrayTypes); - AddKnownArrayTypes> (arrayTypes, javaObjectArrayTypes); - AddKnownArrayTypes (arrayTypes, javaObjectArrayTypes); - } - - static void AddKnownArrayTypes< - [DynamicallyAccessedMembers (Constructors)] - T> (Dictionary arrayTypes, Dictionary javaObjectArrayTypes) - { - arrayTypes [typeof (T)] = typeof (T[]); - arrayTypes [typeof (T[])] = typeof (T[][]); - arrayTypes [typeof (T[][])] = typeof (T[][][]); - javaObjectArrayTypes [typeof (T)] = typeof (JavaObjectArray); - javaObjectArrayTypes [typeof (JavaObjectArray)] = typeof (JavaObjectArray>); - } - - static bool TryMakeArrayType (Type type, out Type? arrayType) => - KnownArrayTypes.Value.ArrayTypes.TryGetValue (type, out arrayType); - - static bool TryMakeJavaObjectArrayType (Type type, out Type? arrayType) => - KnownArrayTypes.Value.JavaObjectArrayTypes.TryGetValue (type, out arrayType); - - 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."); - - [return: DynamicallyAccessedMembers (MethodsConstructors)] - protected override Type? GetTypeForSimpleReference (string jniSimpleReference) - { - AssertValid (); - AssertSimpleReference (jniSimpleReference); - - return jniSimpleReference switch { - "java/lang/String" => TypeOf (), - "V" => TypeOfVoid (), - "Z" => TypeOf (), - "java/lang/Boolean" => TypeOf (), - "B" => TypeOf (), - "java/lang/Byte" => TypeOf (), - "C" => TypeOf (), - "java/lang/Character" => TypeOf (), - "S" => TypeOf (), - "java/lang/Short" => TypeOf (), - "I" => TypeOf (), - "java/lang/Integer" => TypeOf (), - "J" => TypeOf (), - "java/lang/Long" => TypeOf (), - "F" => TypeOf (), - "java/lang/Float" => TypeOf (), - "D" => TypeOf (), - "java/lang/Double" => TypeOf (), - _ => null, - }; - } - - [return: DynamicallyAccessedMembers (MethodsConstructors)] - static Type TypeOf< - [DynamicallyAccessedMembers (MethodsConstructors)] - T> () => typeof (T); - - [return: DynamicallyAccessedMembers (MethodsConstructors)] - static Type TypeOfVoid () => typeof (void); - - public override IEnumerable GetTypes (JniTypeSignature typeSignature) - { - AssertValid (); - - if (typeSignature.SimpleReference == null) - return EmptyTypeArray; - 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"))) { - if (typeSignature.ArrayRank == 0) { - yield return type; - continue; - } - - if (typeSignature.IsKeyword) { - foreach (var t in GetPrimitiveArrayTypesForSimpleReference (typeSignature, type)) { - yield return t; - } - continue; - } - - if (typeSignature.ArrayRank > 0) { - var rank = typeSignature.ArrayRank; - var arrayType = type; - while (rank-- > 0) { - arrayType = TryMakeJavaObjectArrayType (arrayType, out var nextArrayType) - ? nextArrayType ?? throw new InvalidOperationException ("Should not be reached") - : GetUnsupportedJavaObjectArrayType (arrayType); - } - yield return arrayType; - } - - if (typeSignature.ArrayRank > 0) { - var rank = typeSignature.ArrayRank; - var arrayType = type; - while (rank-- > 0) { - arrayType = TryMakeArrayType (arrayType, out var nextArrayType) - ? nextArrayType ?? throw new InvalidOperationException ("Should not be reached") - : GetUnsupportedArrayType (arrayType); - } - yield return arrayType; - } - } - } - - IEnumerable GetPrimitiveArrayTypesForSimpleReference (JniTypeSignature typeSignature, Type type) - { - int index = -1; - for (int i = 0; i < JniPrimitiveArrayTypes.Length; ++i) { - if (JniPrimitiveArrayTypes [i].PrimitiveType == type) { - index = i; - break; - } - } - if (index == -1) { - throw new InvalidOperationException ($"Should not be reached; Could not find JniPrimitiveArrayInfo for {type}"); - } - foreach (var t in JniPrimitiveArrayTypes [index].ArrayTypes) { - var rank = typeSignature.ArrayRank-1; - var arrayType = t; - while (rank-- > 0) { - 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 = TryMakeArrayType (arrayType, out var nextArrayType) - ? nextArrayType ?? throw new InvalidOperationException ("Should not be reached") - : GetUnsupportedArrayType (arrayType); - } - yield return arrayType; - } - } - - protected override IEnumerable GetTypesForSimpleReference (string jniSimpleReference) - { - AssertValid (); - AssertSimpleReference (jniSimpleReference); - - // Not sure why CS8604 is reported on following line when we check against null ~9 lines above... - return CreateGetTypesForSimpleReferenceEnumerator (jniSimpleReference!); - } - - IEnumerable CreateGetTypesForSimpleReferenceEnumerator (string jniSimpleReference) - { - if (JniBuiltinSimpleReferenceToType.Value.TryGetValue (jniSimpleReference, out var ret)) { - yield return ret; - } - if (RuntimeFeature.ManagedPeerNativeRegistration && jniSimpleReference == ManagedPeer.JniTypeName) { - yield return typeof (ManagedPeer); - } - yield break; - } - - [return: DynamicallyAccessedMembers (Constructors)] - protected override Type? GetInvokerTypeCore ( - [DynamicallyAccessedMembers (Constructors)] - Type type) - { - var signature = type.GetCustomAttribute (); - if (signature == null || signature.InvokerType == null) { - return null; - } - - Type[] arguments = type.GetGenericArguments (); - if (arguments.Length == 0) - return signature.InvokerType; - -#if NET - throw new NotSupportedException ($"Generic invoker type construction for `{type}` is not supported."); -#else // NET - return signature.InvokerType.MakeGenericType (arguments); -#endif // NET - } - -#if NET - - protected override IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimple) => null; - - protected override string? GetReplacementTypeCore (string jniSimpleReference) => null; - - protected override ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSimpleReference, string jniMethodName, string jniMethodSignature) => null; - - public override void RegisterNativeMembers ( - JniType nativeClass, - [DynamicallyAccessedMembers (MethodsAndPrivateNested)] - Type type, - ReadOnlySpan methods) - { - TryRegisterNativeMembers (nativeClass, type, methods); - } - - protected bool TryRegisterNativeMembers ( - JniType nativeClass, - [DynamicallyAccessedMembers (MethodsAndPrivateNested)] - Type type, - ReadOnlySpan methods) - { - AssertValid (); - -#pragma warning disable CS1717 - methods = methods; -#pragma warning restore CS1717 - - return TryLoadJniMarshalMethods (nativeClass, type, null) || TryRegisterNativeMembers (nativeClass, type, null, null); - } -#endif // NET - -#if NET - [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] -#endif // NET - public override void RegisterNativeMembers ( - JniType nativeClass, - [DynamicallyAccessedMembers (MethodsAndPrivateNested)] - Type type, - string? methods) - { - TryRegisterNativeMembers (nativeClass, type, methods); - } - -#if NET - [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] -#endif // NET - protected bool TryRegisterNativeMembers ( - JniType nativeClass, - [DynamicallyAccessedMembers (MethodsAndPrivateNested)] - Type type, - string? methods) - { - AssertValid (); - - return TryLoadJniMarshalMethods (nativeClass, type, methods) || TryRegisterNativeMembers (nativeClass, type, methods, null); - } - - static Type [] registerMethodParameters = new Type [] { typeof (JniNativeMethodRegistrationArguments) }; - - // https://github.com/xamarin/xamarin-android/blob/5472eec991cc075e4b0c09cd98a2331fb93aa0f3/src/Microsoft.Android.Sdk.ILLink/PreserveRegistrations.cs#L85 - const string MarshalMethods = "'jni_marshal_methods' is preserved by the PreserveRegistrations trimmer step."; - - [UnconditionalSuppressMessage ("Trimming", "IL2072", Justification = MarshalMethods)] - [UnconditionalSuppressMessage ("Trimming", "IL2075", Justification = MarshalMethods)] - bool TryLoadJniMarshalMethods ( - JniType nativeClass, - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] - Type type, - string? methods) - { - var marshalType = type?.GetNestedType ("__<$>_jni_marshal_methods", BindingFlags.NonPublic); - if (marshalType == null) { - return false; - } - - var registerMethod = marshalType.GetMethod ( - name: "__RegisterNativeMembers", - bindingAttr: BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, - binder: null, - callConvention: default, - types: registerMethodParameters, - modifiers: null); - return TryRegisterNativeMembers (nativeClass, marshalType, methods, registerMethod); - } - - static List sharedRegistrations = new List (); - - bool TryRegisterNativeMembers ( - JniType nativeClass, - [DynamicallyAccessedMembers (Methods)] - Type marshalType, - string? methods, - MethodInfo? registerMethod) - { - bool lockTaken = false; - bool rv = false; - - try { - Monitor.TryEnter (sharedRegistrations, ref lockTaken); - List registrations; - if (lockTaken) { - sharedRegistrations.Clear (); - registrations = sharedRegistrations; - } else { - registrations = new List (); - } - JniNativeMethodRegistrationArguments arguments = new JniNativeMethodRegistrationArguments (registrations, methods); - if (registerMethod != null) { - registerMethod.Invoke (null, new object [] { arguments }); - rv = true; - } else - rv = FindAndCallRegisterMethod (marshalType, arguments); - - if (registrations.Count > 0) - nativeClass.RegisterNativeMethods (registrations.ToArray ()); - } finally { - if (lockTaken) { - Monitor.Exit (sharedRegistrations); - } - } - - return rv; - } - - bool FindAndCallRegisterMethod ( - [DynamicallyAccessedMembers (Methods)] - Type marshalType, - JniNativeMethodRegistrationArguments arguments) - { - if (!Runtime.JniAddNativeMethodRegistrationAttributePresent) - return false; - - bool found = false; - - foreach (var methodInfo in marshalType.GetRuntimeMethods ()) { - if (methodInfo.GetCustomAttribute (typeof (JniAddNativeMethodRegistrationAttribute)) == null) { - continue; - } - - var declaringTypeName = methodInfo.DeclaringType?.FullName ?? ""; - - if ((methodInfo.Attributes & MethodAttributes.Static) != MethodAttributes.Static) { - throw new InvalidOperationException ($"The method `{declaringTypeName}.{methodInfo}` marked with [{nameof (JniAddNativeMethodRegistrationAttribute)}] must be static!"); - } - - var register = (Action)methodInfo.CreateDelegate (typeof (Action)); - register (arguments); - - found = true; - } - - return found; - } - - } } } From 6d52b54fa73acbab89b10cc36abcb8cd5f48c7f4 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 8 Jun 2026 13:21:40 +0200 Subject: [PATCH 04/28] Update NativeAOT sample --- .../NativeAotTypeManager.cs | 55 ++----------------- 1 file changed, 6 insertions(+), 49 deletions(-) diff --git a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs index 709098bd9..240102cf4 100644 --- a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs @@ -3,8 +3,7 @@ namespace Hello_NativeAOTFromJNI; -class NativeAotTypeManager : JniRuntime.JniTypeManager { - internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; +class NativeAotTypeManager : JniRuntime.DynamicJniTypeManager { internal const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods; internal const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; @@ -16,32 +15,25 @@ class NativeAotTypeManager : JniRuntime.JniTypeManager { }; #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 type in base.GetTypesForSimpleReference (jniSimpleReference)) + yield return type; } protected override Type? GetTypeForSimpleReference (string jniSimpleReference) { if (typeMappings.TryGetValue (jniSimpleReference, out var target)) return target; - return null; + return base.GetTypeForSimpleReference (jniSimpleReference); } protected override IEnumerable GetSimpleReferences (Type type) { - return CreateSimpleReferencesEnumerator (type); + return base.GetSimpleReferences (type) + .Concat (CreateSimpleReferencesEnumerator (type)); } IEnumerable CreateSimpleReferencesEnumerator (Type type) @@ -54,41 +46,6 @@ IEnumerable CreateSimpleReferencesEnumerator (Type type) } } - 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)] From fa49c9aeee091e748785d4a493feb499f83619f3 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 8 Jun 2026 13:45:28 +0200 Subject: [PATCH 05/28] Simplify --- .../NativeAotTypeManager.cs | 29 ++----------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs index e87001a4a..36c509b46 100644 --- a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs @@ -4,9 +4,8 @@ namespace Java.Interop.Samples.NativeAotFromAndroid; -partial class NativeAotTypeManager : JniRuntime.JniTypeManager { +partial class NativeAotTypeManager : JniRuntime.DynamicJniTypeManager { - internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; internal const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods; internal const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; @@ -40,15 +39,6 @@ public override void RegisterNativeMembers ( 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) { Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}`"); @@ -87,18 +77,9 @@ public override IEnumerable GetTypes (JniTypeSignature typeSignature) 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); + var simpleReference = GetSimpleReferences (type).FirstOrDefault (); return simpleReference == null ? default : new JniTypeSignature (simpleReference, 0, false); } @@ -108,10 +89,4 @@ protected override IEnumerable GetTypeSignaturesCore (Type typ 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; } From 17e87cd156b7c0d6cac93a81f91d8c6ae57cda35 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 8 Jun 2026 15:38:15 +0200 Subject: [PATCH 06/28] Fix dynamic JniTypeManager test regressions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../JniRuntime.DynamicJniTypeManager.cs | 15 +++++++++++---- .../Java.Interop/JavaVMFixture.cs | 1 + .../JniRuntime.JniTypeManagerTests.cs | 1 + .../Java.Interop/JniRuntimeTest.cs | 1 + 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.DynamicJniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.DynamicJniTypeManager.cs index c3a20953d..d4bda1aac 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.DynamicJniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.DynamicJniTypeManager.cs @@ -205,6 +205,8 @@ static Type GetUnsupportedJavaObjectArrayType (Type type) => return jniSimpleReference switch { "java/lang/String" => TypeOf (), + "net/dot/jni/internal/JavaProxyObject" => typeof (JavaProxyObject), + "net/dot/jni/internal/JavaProxyThrowable" => typeof (JavaProxyThrowable), "V" => TypeOfVoid (), "Z" => TypeOf (), "java/lang/Boolean" => TypeOf (), @@ -306,12 +308,17 @@ IEnumerable GetPrimitiveArrayTypesForSimpleReference (JniTypeSignature typ foreach (var t in JniPrimitiveArrayTypes [index].ArrayTypes) { var rank = typeSignature.ArrayRank-1; var arrayType = t; + var unsupported = false; while (rank-- > 0) { - arrayType = TryMakeJavaObjectArrayType (arrayType, out var nextArrayType) - ? nextArrayType ?? throw new InvalidOperationException ("Should not be reached") - : GetUnsupportedJavaObjectArrayType (arrayType); + if (!TryMakeJavaObjectArrayType (arrayType, out var nextArrayType)) { + unsupported = true; + break; + } + arrayType = nextArrayType ?? throw new InvalidOperationException ("Should not be reached"); + } + if (!unsupported) { + yield return arrayType; } - yield return arrayType; rank = typeSignature.ArrayRank-1; arrayType = t; diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs index b3dc71eaf..e5280d038 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs @@ -52,6 +52,7 @@ class JavaVMFixtureTypeManager : JniRuntime.DynamicJniTypeManager { }; [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Tests intentionally use the default reflection-based type manager.")] + [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Tests intentionally use the default reflection-based type manager.")] public JavaVMFixtureTypeManager () { } diff --git a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs index 9d7c6eb41..f81748709 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs @@ -39,6 +39,7 @@ public void GetInvokerType_GenericType_Throws () class MyTypeManager : JniRuntime.DynamicJniTypeManager { [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Tests intentionally use the default reflection-based type manager.")] + [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Tests intentionally use the default reflection-based type manager.")] public MyTypeManager () { } diff --git a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs index 76b4cfeb1..36cec1552 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs @@ -192,6 +192,7 @@ public override void WaitForGCBridgeProcessing () class ProxyTypeManager : DynamicJniTypeManager { [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Tests intentionally use the default reflection-based type manager.")] + [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Tests intentionally use the default reflection-based type manager.")] public ProxyTypeManager () { } From 7f9798e77f4923069fe76d53b10bd5009cc30ccd Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 8 Jun 2026 16:32:12 +0200 Subject: [PATCH 07/28] Use annotations for dynamic type manager tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Java.Interop/JavaVMFixture.Partial.cs | 23 +++++++++++++------ .../Java.Interop/JavaVMFixture.cs | 4 ++-- .../JniRuntime.JniTypeManagerTests.cs | 8 +++++-- .../Java.Interop/JniRuntimeTest.cs | 10 ++++++-- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs index ece68972e..0c426bca5 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs @@ -1,17 +1,16 @@ -using System; +using System; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using Java.Interop; +using NUnit.Framework; namespace Java.InteropTests { public abstract partial class JavaVMFixture { - static JavaVMFixture () - { - CreateJavaVM (); - } - + [RequiresDynamicCode ("JavaVMFixture uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("JavaVMFixture uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] static partial void CreateJavaVM (); // VM supports specifying a class to JNIEnv::CallNonvirtualVoidMethod() @@ -23,6 +22,16 @@ static JavaVMFixture () protected JavaVMFixture () { } + + + [OneTimeSetUp] + [RequiresDynamicCode ("JavaVMFixture uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("JavaVMFixture uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] + public void EnsureJavaVM () + { + if (VM == null) { + CreateJavaVM (); + } + } } } - diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs index e5280d038..b150076e7 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs @@ -51,8 +51,8 @@ class JavaVMFixtureTypeManager : JniRuntime.DynamicJniTypeManager { [MyJavaInterfaceImpl.JniTypeName] = typeof (MyJavaInterfaceImpl), }; - [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Tests intentionally use the default reflection-based type manager.")] - [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Tests intentionally use the default reflection-based type manager.")] + [RequiresDynamicCode ("JavaVMFixtureTypeManager uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("JavaVMFixtureTypeManager uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] public JavaVMFixtureTypeManager () { } diff --git a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs index f81748709..5864e7418 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs @@ -13,6 +13,8 @@ namespace Java.InteropTests { public class JniRuntimeJniTypeManagerTests : JavaVMFixture { [Test] + [RequiresDynamicCode ("This test uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("This test uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] public void GetInvokerType () { using (var vm = new MyTypeManager ()) { @@ -29,6 +31,8 @@ public void GetInvokerType () #if NET [Test] + [RequiresDynamicCode ("This test uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("This test uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] public void GetInvokerType_GenericType_Throws () { using (var vm = new MyTypeManager ()) { @@ -38,8 +42,8 @@ public void GetInvokerType_GenericType_Throws () #endif // NET class MyTypeManager : JniRuntime.DynamicJniTypeManager { - [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Tests intentionally use the default reflection-based type manager.")] - [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Tests intentionally use the default reflection-based type manager.")] + [RequiresDynamicCode ("MyTypeManager uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("MyTypeManager uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] public MyTypeManager () { } diff --git a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs index 36cec1552..54a49cf6d 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs @@ -98,6 +98,8 @@ public JavaVMWithNullBuilder () } [Test] + [RequiresDynamicCode ("This test uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("This test uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] public void Dispose_ClearsJniEnvironment () { var c = JniRuntime.CurrentRuntime; @@ -129,12 +131,16 @@ class JniProxyRuntime : JniRuntime { JniRuntime Proxy; + [RequiresDynamicCode ("JniProxyRuntime uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("JniProxyRuntime uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] public JniProxyRuntime (JniRuntime proxy) : base (CreateOptions (proxy)) { Proxy = proxy; } + [RequiresDynamicCode ("JniProxyRuntime uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("JniProxyRuntime uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] static JniRuntime.CreationOptions CreateOptions (JniRuntime proxy) { return new JniRuntime.CreationOptions { @@ -191,8 +197,8 @@ public override void WaitForGCBridgeProcessing () } class ProxyTypeManager : DynamicJniTypeManager { - [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Tests intentionally use the default reflection-based type manager.")] - [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Tests intentionally use the default reflection-based type manager.")] + [RequiresDynamicCode ("ProxyTypeManager uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("ProxyTypeManager uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] public ProxyTypeManager () { } From 642936686b539919e2368f4e13297ac6bc256aab Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 8 Jun 2026 17:44:18 +0200 Subject: [PATCH 08/28] Keep NativeAOT samples off dynamic type manager Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../NativeAotTypeManager.cs | 63 ++++++++++- samples/Hello-NativeAOTFromJNI/ManagedType.cs | 6 +- .../NativeAotTypeManager.cs | 104 ++++++++++++++---- .../JniRuntime.DynamicJniTypeManager.cs | 35 +++--- 4 files changed, 155 insertions(+), 53 deletions(-) diff --git a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs index 36c509b46..069bbc0b8 100644 --- a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs @@ -4,10 +4,11 @@ namespace Java.Interop.Samples.NativeAotFromAndroid; -partial class NativeAotTypeManager : JniRuntime.DynamicJniTypeManager { +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; Dictionary typeMappings = new () { ["android/app/Activity"] = typeof (Android.App.Activity), @@ -30,6 +31,7 @@ public override void RegisterNativeMembers ( 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)] @@ -42,19 +44,38 @@ public override void RegisterNativeMembers ( protected override IEnumerable GetTypesForSimpleReference (string jniSimpleReference) { Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}`"); - if (typeMappings.TryGetValue (jniSimpleReference, out var target)) { + var target = GetTypeForSimpleReference (jniSimpleReference); + if (target != null) { Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}` -> `{target}`"); yield return target; } } + protected override string? GetSimpleReference (Type type) + { + return GetSimpleReferences (type).FirstOrDefault (); + } + + [return: DynamicallyAccessedMembers (MethodsConstructors)] protected override Type? GetTypeForSimpleReference (string jniSimpleReference) { - if (typeMappings.TryGetValue (jniSimpleReference, out var target)) - return target; - return null; + return jniSimpleReference switch { + "android/app/Activity" => TypeOf (), + "android/content/Context" => TypeOf (), + "android/content/ContextWrapper" => TypeOf (), + "android/os/BaseBundle" => TypeOf (), + "android/os/Bundle" => TypeOf (), + "android/view/ContextThemeWrapper" => TypeOf (), + "my/MainActivity" => TypeOf (), + _ => null, + }; } + [return: DynamicallyAccessedMembers (MethodsConstructors)] + static Type TypeOf< + [DynamicallyAccessedMembers (MethodsConstructors)] + T> () => typeof (T); + protected override IEnumerable GetSimpleReferences (Type type) { return CreateSimpleReferencesEnumerator (type); @@ -77,6 +98,15 @@ public override IEnumerable GetTypes (JniTypeSignature typeSignature) 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 (); @@ -89,4 +119,27 @@ protected override IEnumerable GetTypeSignaturesCore (Type typ 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/ManagedType.cs b/samples/Hello-NativeAOTFromJNI/ManagedType.cs index d3451f352..3f2cd9d1d 100644 --- a/samples/Hello-NativeAOTFromJNI/ManagedType.cs +++ b/samples/Hello-NativeAOTFromJNI/ManagedType.cs @@ -3,6 +3,9 @@ namespace Example; using System; using Java.Interop; +[System.Runtime.InteropServices.UnmanagedFunctionPointer (System.Runtime.InteropServices.CallingConvention.Winapi)] +delegate IntPtr _JniMarshal_PP_L (IntPtr jnienv, IntPtr n_self); + [JniTypeSignature (JniTypeName)] class ManagedType : Java.Lang.Object { internal const string JniTypeName = "example/ManagedType"; @@ -21,9 +24,6 @@ public Java.Lang.String GetString () return new Java.Lang.String ($"Hello from C#, via Java.Interop! Value={value}"); } - [System.Runtime.InteropServices.UnmanagedFunctionPointer (System.Runtime.InteropServices.CallingConvention.Winapi)] - delegate IntPtr _JniMarshal_PP_L (IntPtr jnienv, IntPtr n_self); - static IntPtr n_GetString (IntPtr jnienv, IntPtr n_self) { var r_self = new JniObjectReference (n_self); diff --git a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs index 240102cf4..4aed22def 100644 --- a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs @@ -3,47 +3,104 @@ namespace Hello_NativeAOTFromJNI; -class NativeAotTypeManager : JniRuntime.DynamicJniTypeManager { +class NativeAotTypeManager : JniRuntime.JniTypeManager { 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 + internal const DynamicallyAccessedMemberTypes MethodsConstructors = MethodsAndPrivateNested | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; protected override IEnumerable GetTypesForSimpleReference (string jniSimpleReference) { - if (typeMappings.TryGetValue (jniSimpleReference, out var target)) + var target = GetTypeForSimpleReference (jniSimpleReference); + if (target != null) yield return target; - foreach (var type in base.GetTypesForSimpleReference (jniSimpleReference)) - yield return type; } + [return: DynamicallyAccessedMembers (MethodsConstructors)] protected override Type? GetTypeForSimpleReference (string jniSimpleReference) { - if (typeMappings.TryGetValue (jniSimpleReference, out var target)) - return target; - return base.GetTypeForSimpleReference (jniSimpleReference); + return jniSimpleReference switch { + Example.ManagedType.JniTypeName => TypeOf (), + "java/lang/Object" => TypeOf (), + "java/lang/String" => TypeOf (), + _ => null, + }; + } + + [return: DynamicallyAccessedMembers (MethodsConstructors)] + static Type TypeOf< + [DynamicallyAccessedMembers (MethodsConstructors)] + T> () => typeof (T); + + 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 base.GetSimpleReferences (type) - .Concat (CreateSimpleReferencesEnumerator (type)); + return CreateSimpleReferencesEnumerator (type); } IEnumerable CreateSimpleReferencesEnumerator (Type type) { - if (typeMappings == null) - yield break; - foreach (var e in typeMappings) { - if (e.Value == type) - yield return e.Key; - } + 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 (); + } + + 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 ( @@ -64,6 +121,7 @@ public override void RegisterNativeMembers ( nativeClass.RegisterNativeMethods (registrations.ToArray ()); } + [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] public override void RegisterNativeMembers ( JniType nativeClass, [DynamicallyAccessedMembers (MethodsAndPrivateNested)] diff --git a/src/Java.Interop/Java.Interop/JniRuntime.DynamicJniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.DynamicJniTypeManager.cs index d4bda1aac..48492c5ab 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.DynamicJniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.DynamicJniTypeManager.cs @@ -191,11 +191,15 @@ static bool TryMakeArrayType (Type type, out Type? 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 MakeArrayType (Type type) => + TryMakeArrayType (type, out var arrayType) + ? arrayType ?? throw new InvalidOperationException ("Should not be reached") + : type.MakeArrayType (); - static Type GetUnsupportedJavaObjectArrayType (Type type) => - throw new NotSupportedException ($"Generic Java array wrapper type construction for `{type}` is not supported."); + static Type MakeJavaObjectArrayType (Type type) => + TryMakeJavaObjectArrayType (type, out var arrayType) + ? arrayType ?? throw new InvalidOperationException ("Should not be reached") + : typeof (JavaObjectArray<>).MakeGenericType (type); [return: DynamicallyAccessedMembers (MethodsConstructors)] protected override Type? GetTypeForSimpleReference (string jniSimpleReference) @@ -273,9 +277,7 @@ IEnumerable CreateGetTypesEnumerator (JniTypeSignature typeSignature) var rank = typeSignature.ArrayRank; var arrayType = type; while (rank-- > 0) { - arrayType = TryMakeJavaObjectArrayType (arrayType, out var nextArrayType) - ? nextArrayType ?? throw new InvalidOperationException ("Should not be reached") - : GetUnsupportedJavaObjectArrayType (arrayType); + arrayType = MakeJavaObjectArrayType (arrayType); } yield return arrayType; } @@ -284,9 +286,7 @@ IEnumerable CreateGetTypesEnumerator (JniTypeSignature typeSignature) var rank = typeSignature.ArrayRank; var arrayType = type; while (rank-- > 0) { - arrayType = TryMakeArrayType (arrayType, out var nextArrayType) - ? nextArrayType ?? throw new InvalidOperationException ("Should not be reached") - : GetUnsupportedArrayType (arrayType); + arrayType = MakeArrayType (arrayType); } yield return arrayType; } @@ -308,24 +308,15 @@ IEnumerable GetPrimitiveArrayTypesForSimpleReference (JniTypeSignature typ foreach (var t in JniPrimitiveArrayTypes [index].ArrayTypes) { var rank = typeSignature.ArrayRank-1; var arrayType = t; - var unsupported = false; while (rank-- > 0) { - if (!TryMakeJavaObjectArrayType (arrayType, out var nextArrayType)) { - unsupported = true; - break; - } - arrayType = nextArrayType ?? throw new InvalidOperationException ("Should not be reached"); - } - if (!unsupported) { - yield return arrayType; + arrayType = MakeJavaObjectArrayType (arrayType); } + yield return arrayType; rank = typeSignature.ArrayRank-1; arrayType = t; while (rank-- > 0) { - arrayType = TryMakeArrayType (arrayType, out var nextArrayType) - ? nextArrayType ?? throw new InvalidOperationException ("Should not be reached") - : GetUnsupportedArrayType (arrayType); + arrayType = MakeArrayType (arrayType); } yield return arrayType; } From c18bdc7e88682f4aa88fa016be9757ef7bb78216 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 8 Jun 2026 17:48:29 +0200 Subject: [PATCH 09/28] Revert extra JniTypeManager test assertions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs b/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs index a028aef2e..198cb9c97 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs @@ -137,11 +137,6 @@ 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;")); From a19e758ce4ee59a51f15ecec72aaf6316f385fa2 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 8 Jun 2026 17:51:46 +0200 Subject: [PATCH 10/28] Move dynamic test annotations to classes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs | 4 ++-- .../Java.Interop/JniRuntime.JniTypeManagerTests.cs | 4 ++-- tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs index b150076e7..bef6fc64d 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs @@ -29,6 +29,8 @@ static partial void CreateJavaVM () } } + [RequiresDynamicCode ("JavaVMFixtureTypeManager uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("JavaVMFixtureTypeManager uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] class JavaVMFixtureTypeManager : JniRuntime.DynamicJniTypeManager { Dictionary TypeMappings = new() { @@ -51,8 +53,6 @@ class JavaVMFixtureTypeManager : JniRuntime.DynamicJniTypeManager { [MyJavaInterfaceImpl.JniTypeName] = typeof (MyJavaInterfaceImpl), }; - [RequiresDynamicCode ("JavaVMFixtureTypeManager uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("JavaVMFixtureTypeManager uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] public JavaVMFixtureTypeManager () { } diff --git a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs index 5864e7418..620e11894 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs @@ -41,9 +41,9 @@ public void GetInvokerType_GenericType_Throws () } #endif // NET + [RequiresDynamicCode ("MyTypeManager uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("MyTypeManager uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] class MyTypeManager : JniRuntime.DynamicJniTypeManager { - [RequiresDynamicCode ("MyTypeManager uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("MyTypeManager uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] public MyTypeManager () { } diff --git a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs index 54a49cf6d..e9100e62b 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs @@ -196,9 +196,9 @@ public override void WaitForGCBridgeProcessing () } } + [RequiresDynamicCode ("ProxyTypeManager uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("ProxyTypeManager uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] class ProxyTypeManager : DynamicJniTypeManager { - [RequiresDynamicCode ("ProxyTypeManager uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("ProxyTypeManager uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] public ProxyTypeManager () { } From cd635d8059708efd2a7a16400cad9a4e6c6a5b63 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 8 Jun 2026 18:08:08 +0200 Subject: [PATCH 11/28] Remove extra generic invoker test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../JniRuntime.JniTypeManagerTests.cs | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs index 620e11894..579de1475 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs @@ -1,6 +1,3 @@ -using System; -using System.Reflection; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Java.Interop; @@ -29,18 +26,6 @@ public void GetInvokerType () } } -#if NET - [Test] - [RequiresDynamicCode ("This test uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("This test uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] - public void GetInvokerType_GenericType_Throws () - { - using (var vm = new MyTypeManager ()) { - Assert.Throws (() => vm.GetInvokerType (typeof (IGenericJavaInterface))); - } - } -#endif // NET - [RequiresDynamicCode ("MyTypeManager uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] [RequiresUnreferencedCode ("MyTypeManager uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] class MyTypeManager : JniRuntime.DynamicJniTypeManager { @@ -48,18 +33,5 @@ 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) - { - } - } } } From 6af232fbcf19312ffc53d23c64d8d48007388614 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 8 Jun 2026 18:22:13 +0200 Subject: [PATCH 12/28] Use direct type references in NativeAOT samples Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../NativeAotTypeManager.cs | 19 +++++++------------ .../NativeAotTypeManager.cs | 11 +++-------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs index 069bbc0b8..2e8b28998 100644 --- a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs @@ -60,22 +60,17 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl protected override Type? GetTypeForSimpleReference (string jniSimpleReference) { return jniSimpleReference switch { - "android/app/Activity" => TypeOf (), - "android/content/Context" => TypeOf (), - "android/content/ContextWrapper" => TypeOf (), - "android/os/BaseBundle" => TypeOf (), - "android/os/Bundle" => TypeOf (), - "android/view/ContextThemeWrapper" => TypeOf (), - "my/MainActivity" => TypeOf (), + "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, }; } - [return: DynamicallyAccessedMembers (MethodsConstructors)] - static Type TypeOf< - [DynamicallyAccessedMembers (MethodsConstructors)] - T> () => typeof (T); - protected override IEnumerable GetSimpleReferences (Type type) { return CreateSimpleReferencesEnumerator (type); diff --git a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs index 4aed22def..baa89a579 100644 --- a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs @@ -19,18 +19,13 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl protected override Type? GetTypeForSimpleReference (string jniSimpleReference) { return jniSimpleReference switch { - Example.ManagedType.JniTypeName => TypeOf (), - "java/lang/Object" => TypeOf (), - "java/lang/String" => TypeOf (), + Example.ManagedType.JniTypeName => typeof (Example.ManagedType), + "java/lang/Object" => typeof (Java.Lang.Object), + "java/lang/String" => typeof (Java.Lang.String), _ => null, }; } - [return: DynamicallyAccessedMembers (MethodsConstructors)] - static Type TypeOf< - [DynamicallyAccessedMembers (MethodsConstructors)] - T> () => typeof (T); - public override IEnumerable GetTypes (JniTypeSignature typeSignature) { if (!typeSignature.IsValid || typeSignature.ArrayRank != 0 || typeSignature.SimpleReference == null) From 906e8dd8c4cfc74999fb182bf14dba4a010e367d Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 8 Jun 2026 20:07:08 +0200 Subject: [PATCH 13/28] Separate default JRE type manager from AOT path Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../JavaInteropRuntime.cs | 3 +- .../JavaInteropRuntime.cs | 3 +- .../Java.Interop/JreRuntime.cs | 50 +++++++++++++++++-- .../Java.Interop/JreTypeManager.cs | 2 + .../Java.Interop/JniRuntimeTest.cs | 4 ++ 5 files changed, 54 insertions(+), 8 deletions(-) diff --git a/samples/Hello-NativeAOTFromAndroid/JavaInteropRuntime.cs b/samples/Hello-NativeAOTFromAndroid/JavaInteropRuntime.cs index f6595cdc1..3b22b56d6 100644 --- a/samples/Hello-NativeAOTFromAndroid/JavaInteropRuntime.cs +++ b/samples/Hello-NativeAOTFromAndroid/JavaInteropRuntime.cs @@ -37,11 +37,10 @@ static void init (IntPtr jnienv, IntPtr klass) try { var options = new JreRuntimeOptions { EnvironmentPointer = jnienv, - TypeManager = new NativeAotTypeManager (), JniGlobalReferenceLogWriter = new LogcatTextWriter (AndroidLogLevel.Debug, "NativeAot:GREF"), JniLocalReferenceLogWriter = new LogcatTextWriter (AndroidLogLevel.Debug, "NativeAot:LREF"), }; - runtime = options.CreateJreVM (); + runtime = options.CreateJreVM (new NativeAotTypeManager ()); } catch (Exception e) { AndroidLog.Print (AndroidLogLevel.Error, "JavaInteropRuntime", $"JavaInteropRuntime.init: error: {e}"); diff --git a/samples/Hello-NativeAOTFromJNI/JavaInteropRuntime.cs b/samples/Hello-NativeAOTFromJNI/JavaInteropRuntime.cs index 70fe0ba7e..130264814 100644 --- a/samples/Hello-NativeAOTFromJNI/JavaInteropRuntime.cs +++ b/samples/Hello-NativeAOTFromJNI/JavaInteropRuntime.cs @@ -28,9 +28,8 @@ static void init (IntPtr jnienv, IntPtr klass) try { var options = new JreRuntimeOptions { EnvironmentPointer = jnienv, - TypeManager = new NativeAotTypeManager (), }; - runtime = options.CreateJreVM (); + runtime = options.CreateJreVM (new NativeAotTypeManager ()); } catch (Exception e) { Console.Error.WriteLine ($"JavaInteropRuntime.init: error: {e}"); diff --git a/src/Java.Runtime.Environment/Java.Interop/JreRuntime.cs b/src/Java.Runtime.Environment/Java.Interop/JreRuntime.cs index dd0912bf4..70e6f87d3 100644 --- a/src/Java.Runtime.Environment/Java.Interop/JreRuntime.cs +++ b/src/Java.Runtime.Environment/Java.Interop/JreRuntime.cs @@ -64,10 +64,19 @@ public JreRuntimeOptions AddSystemProperty (string name, string value) return this; } + [RequiresDynamicCode ("The default JRE type manager is reflection-based and is not compatible with Native AOT. Use CreateJreVM(JniRuntime.JniTypeManager) with an AOT-safe type manager instead.")] + [RequiresUnreferencedCode ("The default JRE type manager is reflection-based and is not trimming-compatible. Use CreateJreVM(JniRuntime.JniTypeManager) with a trimming-safe type manager instead.")] public JreRuntime CreateJreVM () { return new JreRuntime (this); } + + public JreRuntime CreateJreVM (JniRuntime.JniTypeManager typeManager) + { + if (typeManager == null) + throw new ArgumentNullException (nameof (typeManager)); + return new JreRuntime (this, typeManager); + } } public class JreRuntime : JniRuntime @@ -76,10 +85,29 @@ static JreRuntime () { } + [RequiresDynamicCode ("The default JRE type manager is reflection-based and is not compatible with Native AOT.")] + [RequiresUnreferencedCode ("The default JRE type manager is reflection-based and is not trimming-compatible.")] static unsafe JreRuntimeOptions CreateJreVM (JreRuntimeOptions builder) { if (builder == null) throw new ArgumentNullException ("builder"); +#if NET + builder.TypeManager ??= new JreTypeManager (builder.typeMappings); +#endif // NET + return CreateJreVMCore (builder); + } + + static unsafe JreRuntimeOptions CreateJreVMWithoutDefaultTypeManager (JreRuntimeOptions builder) + { + if (builder == null) + throw new ArgumentNullException ("builder"); + if (builder.TypeManager == null) + throw new InvalidOperationException ($"Member `{nameof (JniRuntime.CreationOptions)}.{nameof (JniRuntime.CreationOptions.TypeManager)}` must be set."); + return CreateJreVMCore (builder); + } + + static unsafe JreRuntimeOptions CreateJreVMCore (JreRuntimeOptions builder) + { if (builder.InvocationPointer == IntPtr.Zero && builder.EnvironmentPointer == IntPtr.Zero && string.IsNullOrEmpty (builder.JvmLibraryPath)) @@ -87,10 +115,6 @@ static unsafe JreRuntimeOptions CreateJreVM (JreRuntimeOptions builder) builder.LibraryHandler = JvmLibraryHandler.Create (); -#if NET - builder.TypeManager ??= new JreTypeManager (builder.typeMappings); -#endif // NET - bool onMono = Type.GetType ("Mono.RuntimeStructs", throwOnError: false) != null; if (onMono) { Console.WriteLine ($"MonoVM support enabled"); @@ -162,12 +186,30 @@ static unsafe JreRuntimeOptions CreateJreVM (JreRuntimeOptions builder) JvmLibraryHandler LibraryHandler; + [RequiresDynamicCode ("The default JRE type manager is reflection-based and is not compatible with Native AOT.")] + [RequiresUnreferencedCode ("The default JRE type manager is reflection-based and is not trimming-compatible.")] internal protected JreRuntime (JreRuntimeOptions builder) : base (CreateJreVM (builder)) { LibraryHandler = builder.LibraryHandler!; } + internal protected JreRuntime (JreRuntimeOptions builder, JniRuntime.JniTypeManager typeManager) + : base (CreateJreVMWithoutDefaultTypeManager (SetTypeManager (builder, typeManager))) + { + LibraryHandler = builder.LibraryHandler!; + } + + static JreRuntimeOptions SetTypeManager (JreRuntimeOptions builder, JniRuntime.JniTypeManager typeManager) + { + if (builder == null) + throw new ArgumentNullException ("builder"); + if (typeManager == null) + throw new ArgumentNullException (nameof (typeManager)); + builder.TypeManager = typeManager; + return builder; + } + public override string? GetCurrentManagedThreadName () { return Thread.CurrentThread.Name; diff --git a/src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs b/src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs index 98657366a..47748e1ce 100644 --- a/src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs +++ b/src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs @@ -9,6 +9,8 @@ namespace Java.Interop { + [RequiresDynamicCode ("JreTypeManager uses DynamicJniTypeManager reflection-based behavior and is not compatible with Native AOT.")] + [RequiresUnreferencedCode ("JreTypeManager uses DynamicJniTypeManager reflection-based behavior and is not trimming-compatible.")] public class JreTypeManager : JniRuntime.DynamicJniTypeManager { IDictionary? typeMappings; diff --git a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs index e9100e62b..c4dadaa2d 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs @@ -27,6 +27,8 @@ public void CreateJavaVM () #if !__ANDROID__ [Test] + [RequiresDynamicCode ("This test intentionally uses the default JRE type manager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("This test intentionally uses the default JRE type manager, which is reflection-based and not trimming-compatible.")] public void JDK_OnlySupportsOneVM () { try { @@ -43,6 +45,8 @@ public void JDK_OnlySupportsOneVM () } [Test] + [RequiresDynamicCode ("This test intentionally uses the default JRE type manager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("This test intentionally uses the default JRE type manager, which is reflection-based and not trimming-compatible.")] public void UseInvocationPointerOnNewThread () { var InvocationPointer = JniRuntime.CurrentRuntime.InvocationPointer; From ef5d9c8b52e543731e6bf5ea117e1686aa097666 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 9 Jun 2026 07:34:03 +0200 Subject: [PATCH 14/28] Handle built-in proxy types in AOT type managers Map Java.Interop's internal proxy peer types in the base type manager so custom AOT-safe managers do not need reflection-based DynamicJniTypeManager behavior for JavaProxyThrowable/JavaProxyObject. Register built-in proxy native members by JNI type name before asking custom managers for a managed Type, avoiding DAM-annotated lookups of internal Exception/Delegate-derived types. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../NativeAotTypeManager.cs | 6 +--- .../Java.Interop/JavaProxyObject.cs | 2 +- .../Java.Interop/JniRuntime.JniTypeManager.cs | 34 ++++++++++++++++++- src/Java.Interop/Java.Interop/ManagedPeer.cs | 8 ++++- 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs index 2e8b28998..acb33d7a4 100644 --- a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs @@ -26,7 +26,6 @@ public override void RegisterNativeMembers ( Type type, ReadOnlySpan methods) { - Console.WriteLine ($"# jonp: RegisterNativeMembers: nativeClass={nativeClass} type=`{type}`"); if (!methods.IsEmpty) throw new NotSupportedException ($"Could not register native members for type '{type.FullName}'."); } @@ -43,12 +42,9 @@ public override void RegisterNativeMembers ( protected override IEnumerable GetTypesForSimpleReference (string jniSimpleReference) { - Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}`"); var target = GetTypeForSimpleReference (jniSimpleReference); - if (target != null) { - Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}` -> `{target}`"); + if (target != null) yield return target; - } } protected override string? GetSimpleReference (Type type) diff --git a/src/Java.Interop/Java.Interop/JavaProxyObject.cs b/src/Java.Interop/Java.Interop/JavaProxyObject.cs index 909629b18..0050cf291 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] - static void RegisterNativeMembers (JniNativeMethodRegistrationArguments args) + internal 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 e541947bf..f1d1f73bf 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs @@ -146,7 +146,8 @@ public JniTypeSignature GetTypeSignature (Type type) if (type == null) throw new ArgumentNullException (nameof (type)); - return GetTypeSignatureCore (type); + var builtIn = GetBuiltInTypeSignature (type); + return builtIn.IsValid ? builtIn : GetTypeSignatureCore (type); } protected abstract JniTypeSignature GetTypeSignatureCore (Type type); @@ -159,6 +160,10 @@ public IEnumerable GetTypeSignatures (Type type) if (type == null) return []; + var builtIn = GetBuiltInTypeSignature (type); + if (builtIn.IsValid) + return new [] { builtIn }; + return GetTypeSignaturesCore (type); } @@ -203,6 +208,33 @@ public ReflectionConstructibleType ( protected abstract IEnumerable GetTypesForSimpleReference (string jniSimpleReference); + static JniTypeSignature GetBuiltInTypeSignature (Type type) + { + if (type == typeof (JavaProxyObject)) + return new JniTypeSignature (JavaProxyObject.JniTypeName, 0, false); + if (type == typeof (JavaProxyThrowable)) + return new JniTypeSignature (JavaProxyThrowable.JniTypeName, 0, false); + return default; + } + +#if NET + internal 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; + } +#endif + /// [return: DynamicallyAccessedMembers (Constructors)] public Type? GetInvokerType ( diff --git a/src/Java.Interop/Java.Interop/ManagedPeer.cs b/src/Java.Interop/Java.Interop/ManagedPeer.cs index 56f1c22e7..0eb50269a 100644 --- a/src/Java.Interop/Java.Interop/ManagedPeer.cs +++ b/src/Java.Interop/Java.Interop/ManagedPeer.cs @@ -291,13 +291,18 @@ static unsafe void RegisterNativeMembers ( var methodsRef = new JniObjectReference (n_methods); var typeSig = new JniTypeSignature (nativeClass.Name); - var type = GetTypeFromSignature (JniEnvironment.Runtime.TypeManager, typeSig); #if NET int methodsLength = JniEnvironment.Strings.GetStringLength (methodsRef); var methodsChars = JniEnvironment.Strings.GetStringChars (methodsRef, null); var methods = new ReadOnlySpan(methodsChars, methodsLength); + Type? type = null; try { + if (typeSig.SimpleReference != null && + JniRuntime.JniTypeManager.TryRegisterBuiltInNativeMembers (nativeClass, typeSig.SimpleReference, methods)) + return; + + type = GetTypeFromSignature (JniEnvironment.Runtime.TypeManager, typeSig); JniEnvironment.Runtime.TypeManager.RegisterNativeMembers (nativeClass, type, methods); } catch (Exception e) { @@ -309,6 +314,7 @@ static unsafe void RegisterNativeMembers ( JniEnvironment.Strings.ReleaseStringChars (methodsRef, methodsChars); } #else // NET + var type = GetTypeFromSignature (JniEnvironment.Runtime.TypeManager, typeSig); var methods = JniEnvironment.Strings.ToString (methodsRef); JniEnvironment.Runtime.TypeManager.RegisterNativeMembers (nativeClass, type, methods); #endif // NET From a07fda25fe669f1a95e9a1e8d4948a6f9f3cc931 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 9 Jun 2026 08:18:49 +0200 Subject: [PATCH 15/28] Map primitive signatures for AOT constructor lookup Let the base JniTypeManager resolve primitive JNI keyword signatures without requiring custom AOT-safe managers to duplicate the dynamic type manager's primitive mappings. Use that base lookup as a fallback when matching Java-to-managed constructor signatures so signatures such as (I)V resolve to System.Int32. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Java.Interop/JniRuntime.JniTypeManager.cs | 31 +++++++++++++++++++ src/Java.Interop/Java.Interop/ManagedPeer.cs | 5 +++ 2 files changed, 36 insertions(+) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs index f1d1f73bf..8c015d1ed 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs @@ -177,6 +177,10 @@ public IEnumerable GetTypeSignatures (Type type) if (!typeSignature.IsValid || typeSignature.SimpleReference == null) return null; + var builtIn = GetBuiltInType (typeSignature); + if (builtIn != null) + return builtIn; + var type = GetTypeForSimpleReference (typeSignature.SimpleReference); if (type == null) return null; @@ -217,6 +221,33 @@ static JniTypeSignature GetBuiltInTypeSignature (Type type) return default; } + [return: DynamicallyAccessedMembers (MethodsConstructors)] + static Type? GetBuiltInType (JniTypeSignature typeSignature) + { + if (!typeSignature.IsKeyword || typeSignature.ArrayRank != 0) + return null; + return typeSignature.SimpleReference switch { + "V" => TypeOfVoid (), + "Z" => TypeOf (), + "B" => TypeOf (), + "C" => TypeOf (), + "S" => TypeOf (), + "I" => TypeOf (), + "J" => TypeOf (), + "F" => TypeOf (), + "D" => TypeOf (), + _ => null, + }; + } + + [return: DynamicallyAccessedMembers (MethodsConstructors)] + static Type TypeOf< + [DynamicallyAccessedMembers (MethodsConstructors)] + T> () => typeof (T); + + [return: DynamicallyAccessedMembers (MethodsConstructors)] + static Type TypeOfVoid () => typeof (void); + #if NET internal static bool TryRegisterBuiltInNativeMembers ( JniType nativeClass, diff --git a/src/Java.Interop/Java.Interop/ManagedPeer.cs b/src/Java.Interop/Java.Interop/ManagedPeer.cs index 0eb50269a..a3efd68fb 100644 --- a/src/Java.Interop/Java.Interop/ManagedPeer.cs +++ b/src/Java.Interop/Java.Interop/ManagedPeer.cs @@ -225,6 +225,11 @@ static List[] GetConstructorCandidateParameterTypes (string signature) int i = 0; foreach (var jniType in JniMemberSignature.GetParameterTypesFromMethodSignature (signature)) { var possibleTypes = new List (typeManager.GetTypes (jniType)); + if (possibleTypes.Count == 0 && jniType.ArrayRank == 0) { + var type = typeManager.GetType (jniType); + if (type != null) + possibleTypes.Add (type); + } if (possibleTypes.Count == 0) { throw new NotSupportedException ( $"Could not find System.Type corresponding to Java type `{jniType}` within constructor signature `{signature}`.", From b1f75c8ac59d60a9e9f0a3a0e16dd8342e42ba7e Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 9 Jun 2026 09:31:56 +0200 Subject: [PATCH 16/28] Rename dynamic JNI type manager Rename DynamicJniTypeManager to ReflectionJniTypeManager so the type name describes the reflection-based behavior that is incompatible with Native AOT. Update the matching source file name, API baseline, JRE type manager inheritance, generated primitive-array partial, and tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Java.Interop/GlobalSuppressions.cs | 2 +- .../Java.Interop/JavaPrimitiveArrays.cs | 2 +- .../Java.Interop/JavaPrimitiveArrays.tt | 2 +- ...=> JniRuntime.ReflectionJniTypeManager.cs} | 2 +- src/Java.Interop/PublicAPI.Unshipped.txt | 36 +++++++++---------- .../Java.Interop/JreTypeManager.cs | 6 ++-- .../Java.Interop/JavaVMFixture.Partial.cs | 8 ++--- .../Java.Interop/JavaVMFixture.cs | 6 ++-- .../JniRuntime.JniTypeManagerTests.cs | 10 +++--- .../Java.Interop/JniRuntimeTest.cs | 18 +++++----- 10 files changed, 46 insertions(+), 46 deletions(-) rename src/Java.Interop/Java.Interop/{JniRuntime.DynamicJniTypeManager.cs => JniRuntime.ReflectionJniTypeManager.cs} (99%) diff --git a/src/Java.Interop/GlobalSuppressions.cs b/src/Java.Interop/GlobalSuppressions.cs index 775592df2..ea8438058 100644 --- a/src/Java.Interop/GlobalSuppressions.cs +++ b/src/Java.Interop/GlobalSuppressions.cs @@ -25,7 +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.JniRuntime.ReflectionJniTypeManager")] [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 d18e6dea6..a7f06c76f 100644 --- a/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs +++ b/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs @@ -13,7 +13,7 @@ namespace Java.Interop { partial class JniRuntime { - partial class DynamicJniTypeManager { + partial class ReflectionJniTypeManager { 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 8a3a6d31b..36fef3d04 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 DynamicJniTypeManager { + partial class ReflectionJniTypeManager { readonly struct JniPrimitiveArrayInfo { public readonly JniTypeSignature JniTypeSignature; diff --git a/src/Java.Interop/Java.Interop/JniRuntime.DynamicJniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs similarity index 99% rename from src/Java.Interop/Java.Interop/JniRuntime.DynamicJniTypeManager.cs rename to src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs index 48492c5ab..410e28178 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.DynamicJniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs @@ -16,7 +16,7 @@ public partial class JniRuntime { [RequiresDynamicCode ("This JniTypeManager implementation is not compatible with Native AOT. Use a different JniTypeManager implementation that supports Native AOT.")] [RequiresUnreferencedCode ("This JniTypeManager implementation is not compatible with Native AOT. Use a different JniTypeManager implementation that supports Native AOT.")] - public partial class DynamicJniTypeManager : JniTypeManager { + public partial class ReflectionJniTypeManager : JniTypeManager { protected override JniTypeSignature GetTypeSignatureCore (Type type) { diff --git a/src/Java.Interop/PublicAPI.Unshipped.txt b/src/Java.Interop/PublicAPI.Unshipped.txt index df1a7c8ba..49a9dc38e 100644 --- a/src/Java.Interop/PublicAPI.Unshipped.txt +++ b/src/Java.Interop/PublicAPI.Unshipped.txt @@ -11,10 +11,10 @@ abstract Java.Interop.JniRuntime.JniTypeManager.GetTypeSignaturesCore(System.Typ 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.ReflectionJniTypeManager +Java.Interop.JniRuntime.ReflectionJniTypeManager.ReflectionJniTypeManager() -> void +Java.Interop.JniRuntime.ReflectionJniTypeManager.TryRegisterNativeMembers(Java.Interop.JniType! nativeClass, System.Type! type, string? methods) -> bool +Java.Interop.JniRuntime.ReflectionJniTypeManager.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 @@ -47,17 +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 +override Java.Interop.JniRuntime.ReflectionJniTypeManager.GetInvokerTypeCore(System.Type! type) -> System.Type? +override Java.Interop.JniRuntime.ReflectionJniTypeManager.GetReflectionConstructibleTypes(Java.Interop.JniTypeSignature typeSignature) -> System.Collections.Generic.IEnumerable! +override Java.Interop.JniRuntime.ReflectionJniTypeManager.GetReplacementMethodInfoCore(string! jniSimpleReference, string! jniMethodName, string! jniMethodSignature) -> Java.Interop.JniRuntime.ReplacementMethodInfo? +override Java.Interop.JniRuntime.ReflectionJniTypeManager.GetReplacementTypeCore(string! jniSimpleReference) -> string? +override Java.Interop.JniRuntime.ReflectionJniTypeManager.GetSimpleReference(System.Type! type) -> string? +override Java.Interop.JniRuntime.ReflectionJniTypeManager.GetSimpleReferences(System.Type! type) -> System.Collections.Generic.IEnumerable! +override Java.Interop.JniRuntime.ReflectionJniTypeManager.GetStaticMethodFallbackTypesCore(string! jniSimple) -> System.Collections.Generic.IReadOnlyList? +override Java.Interop.JniRuntime.ReflectionJniTypeManager.GetTypeForSimpleReference(string! jniSimpleReference) -> System.Type? +override Java.Interop.JniRuntime.ReflectionJniTypeManager.GetTypeSignatureCore(System.Type! type) -> Java.Interop.JniTypeSignature +override Java.Interop.JniRuntime.ReflectionJniTypeManager.GetTypeSignaturesCore(System.Type! type) -> System.Collections.Generic.IEnumerable! +override Java.Interop.JniRuntime.ReflectionJniTypeManager.GetTypes(Java.Interop.JniTypeSignature typeSignature) -> System.Collections.Generic.IEnumerable! +override Java.Interop.JniRuntime.ReflectionJniTypeManager.GetTypesForSimpleReference(string! jniSimpleReference) -> System.Collections.Generic.IEnumerable! +override Java.Interop.JniRuntime.ReflectionJniTypeManager.RegisterNativeMembers(Java.Interop.JniType! nativeClass, System.Type! type, string? methods) -> void +override Java.Interop.JniRuntime.ReflectionJniTypeManager.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 47748e1ce..b23de443c 100644 --- a/src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs +++ b/src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs @@ -9,9 +9,9 @@ namespace Java.Interop { - [RequiresDynamicCode ("JreTypeManager uses DynamicJniTypeManager reflection-based behavior and is not compatible with Native AOT.")] - [RequiresUnreferencedCode ("JreTypeManager uses DynamicJniTypeManager reflection-based behavior and is not trimming-compatible.")] - public class JreTypeManager : JniRuntime.DynamicJniTypeManager { + [RequiresDynamicCode ("JreTypeManager uses ReflectionJniTypeManager reflection-based behavior and is not compatible with Native AOT.")] + [RequiresUnreferencedCode ("JreTypeManager uses ReflectionJniTypeManager reflection-based behavior and is not trimming-compatible.")] + public class JreTypeManager : JniRuntime.ReflectionJniTypeManager { IDictionary? typeMappings; diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs index 0c426bca5..6ceb1ff53 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs @@ -9,8 +9,8 @@ namespace Java.InteropTests { public abstract partial class JavaVMFixture { - [RequiresDynamicCode ("JavaVMFixture uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("JavaVMFixture uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] + [RequiresDynamicCode ("JavaVMFixture uses ReflectionJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("JavaVMFixture uses ReflectionJniTypeManager, which is reflection-based and not trimming-compatible.")] static partial void CreateJavaVM (); // VM supports specifying a class to JNIEnv::CallNonvirtualVoidMethod() @@ -25,8 +25,8 @@ protected JavaVMFixture () [OneTimeSetUp] - [RequiresDynamicCode ("JavaVMFixture uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("JavaVMFixture uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] + [RequiresDynamicCode ("JavaVMFixture uses ReflectionJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("JavaVMFixture uses ReflectionJniTypeManager, which is reflection-based and not trimming-compatible.")] public void EnsureJavaVM () { if (VM == null) { diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs index bef6fc64d..b20b6897e 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs @@ -29,9 +29,9 @@ static partial void CreateJavaVM () } } - [RequiresDynamicCode ("JavaVMFixtureTypeManager uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("JavaVMFixtureTypeManager uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] - class JavaVMFixtureTypeManager : JniRuntime.DynamicJniTypeManager { + [RequiresDynamicCode ("JavaVMFixtureTypeManager uses ReflectionJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("JavaVMFixtureTypeManager uses ReflectionJniTypeManager, which is reflection-based and not trimming-compatible.")] + class JavaVMFixtureTypeManager : JniRuntime.ReflectionJniTypeManager { Dictionary TypeMappings = new() { #if !NO_MARSHAL_MEMBER_BUILDER_SUPPORT diff --git a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs index 579de1475..1994a073b 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs @@ -10,8 +10,8 @@ namespace Java.InteropTests { public class JniRuntimeJniTypeManagerTests : JavaVMFixture { [Test] - [RequiresDynamicCode ("This test uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("This test uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] + [RequiresDynamicCode ("This test uses ReflectionJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("This test uses ReflectionJniTypeManager, which is reflection-based and not trimming-compatible.")] public void GetInvokerType () { using (var vm = new MyTypeManager ()) { @@ -26,9 +26,9 @@ public void GetInvokerType () } } - [RequiresDynamicCode ("MyTypeManager uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("MyTypeManager uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] - class MyTypeManager : JniRuntime.DynamicJniTypeManager { + [RequiresDynamicCode ("MyTypeManager uses ReflectionJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("MyTypeManager uses ReflectionJniTypeManager, which is reflection-based and not trimming-compatible.")] + class MyTypeManager : JniRuntime.ReflectionJniTypeManager { public MyTypeManager () { } diff --git a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs index c4dadaa2d..3b3285eb9 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs @@ -102,8 +102,8 @@ public JavaVMWithNullBuilder () } [Test] - [RequiresDynamicCode ("This test uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("This test uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] + [RequiresDynamicCode ("This test uses ReflectionJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("This test uses ReflectionJniTypeManager, which is reflection-based and not trimming-compatible.")] public void Dispose_ClearsJniEnvironment () { var c = JniRuntime.CurrentRuntime; @@ -135,16 +135,16 @@ class JniProxyRuntime : JniRuntime { JniRuntime Proxy; - [RequiresDynamicCode ("JniProxyRuntime uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("JniProxyRuntime uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] + [RequiresDynamicCode ("JniProxyRuntime uses ReflectionJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("JniProxyRuntime uses ReflectionJniTypeManager, which is reflection-based and not trimming-compatible.")] public JniProxyRuntime (JniRuntime proxy) : base (CreateOptions (proxy)) { Proxy = proxy; } - [RequiresDynamicCode ("JniProxyRuntime uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("JniProxyRuntime uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] + [RequiresDynamicCode ("JniProxyRuntime uses ReflectionJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("JniProxyRuntime uses ReflectionJniTypeManager, which is reflection-based and not trimming-compatible.")] static JniRuntime.CreationOptions CreateOptions (JniRuntime proxy) { return new JniRuntime.CreationOptions { @@ -200,9 +200,9 @@ public override void WaitForGCBridgeProcessing () } } - [RequiresDynamicCode ("ProxyTypeManager uses DynamicJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("ProxyTypeManager uses DynamicJniTypeManager, which is reflection-based and not trimming-compatible.")] - class ProxyTypeManager : DynamicJniTypeManager { + [RequiresDynamicCode ("ProxyTypeManager uses ReflectionJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("ProxyTypeManager uses ReflectionJniTypeManager, which is reflection-based and not trimming-compatible.")] + class ProxyTypeManager : ReflectionJniTypeManager { public ProxyTypeManager () { } From 5877194b24a4cd879377dd65f9427db4dd1bd441 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 9 Jun 2026 11:11:33 +0200 Subject: [PATCH 17/28] Split JniValueManager and ReflectionJniValueManager --- src/Java.Interop/GlobalSuppressions.cs | 1 + .../Java.Interop/JniObjectReferenceOptions.cs | 2 +- .../JniRuntime.JniValueManager.cs | 822 ++---------------- .../JniRuntime.ReflectionJniValueManager.cs | 720 +++++++++++++++ src/Java.Interop/PublicAPI.Shipped.txt | 4 +- src/Java.Interop/PublicAPI.Unshipped.txt | 21 +- 6 files changed, 821 insertions(+), 749 deletions(-) create mode 100644 src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs diff --git a/src/Java.Interop/GlobalSuppressions.cs b/src/Java.Interop/GlobalSuppressions.cs index ea8438058..5c9318516 100644 --- a/src/Java.Interop/GlobalSuppressions.cs +++ b/src/Java.Interop/GlobalSuppressions.cs @@ -26,6 +26,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.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.ReflectionJniTypeManager")] +[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.ReflectionJniValueManager")] [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/JniObjectReferenceOptions.cs b/src/Java.Interop/Java.Interop/JniObjectReferenceOptions.cs index 367e4157a..3fcb3f82e 100644 --- a/src/Java.Interop/Java.Interop/JniObjectReferenceOptions.cs +++ b/src/Java.Interop/Java.Interop/JniObjectReferenceOptions.cs @@ -20,7 +20,7 @@ partial struct JniObjectReference { } partial class JniRuntime { - partial class JniValueManager { + partial class ReflectionJniValueManager { const JniObjectReferenceOptions DoNotRegisterTarget = (JniObjectReferenceOptions) (1 << 2); } } diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs index 5913b2f80..174800902 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs @@ -2,17 +2,9 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Linq; -using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading; - -using Java.Interop.Expressions; namespace Java.Interop { @@ -78,92 +70,32 @@ protected virtual void Dispose (bool disposing) disposed = true; } - public abstract void WaitForGCBridgeProcessing (); + protected void EnsureNotDisposed () + { + if (disposed) + throw new ObjectDisposedException (GetType ().Name); + } + public abstract void WaitForGCBridgeProcessing (); public abstract void CollectPeers (); - public abstract void AddPeer (IJavaPeerable value); - public abstract void RemovePeer (IJavaPeerable value); - public abstract void FinalizePeer (IJavaPeerable value); - - public abstract List GetSurfacedPeers (); - - public virtual void ActivatePeer ( + public abstract IJavaPeerable? PeekPeer (JniObjectReference reference); + public abstract List GetSurfacedPeers (); + public abstract void ActivatePeer ( JniObjectReference reference, [DynamicallyAccessedMembers (Constructors)] Type type, ConstructorInfo cinfo, - object?[]? argumentValues) - { - try { - var self = (IJavaPeerable) RuntimeHelpers.GetUninitializedObject (type); - self.SetPeerReference (reference); - cinfo.Invoke (self, argumentValues); - } catch (Exception e) { - var m = string.Format ( - CultureInfo.InvariantCulture, - "Could not activate {{ PeerReference={0} IdentityHashCode=0x{1} Java.Type={2} }} for managed type '{3}'.", - reference, - GetJniIdentityHashCode (reference).ToString ("x", CultureInfo.InvariantCulture), - JniEnvironment.Types.GetJniTypeNameFromInstance (reference), - type.FullName); - Debug.WriteLine (m); - - throw new NotSupportedException (m, e); - } - } + object?[]? argumentValues); public void ConstructPeer (IJavaPeerable peer, ref JniObjectReference reference, JniObjectReferenceOptions options) { - if (peer == null) - throw new ArgumentNullException (nameof (peer)); - - var newRef = peer.PeerReference; - if (newRef.IsValid) { - JniObjectReference.Dispose (ref reference, options); - - // Activation? See ManagedPeer.Construct, CreatePeer - // Instance was already added, don't add again - if (peer.JniManagedPeerState.HasFlag (JniManagedPeerStates.Activatable)) { - return; - } - var orig = newRef; - newRef = orig.NewGlobalRef (); - JniObjectReference.Dispose (ref orig); - } else if (options == JniObjectReferenceOptions.None) { - // `reference` is likely *InvalidJniObjectReference, and can't be touched - return; - } else if (!reference.IsValid) { - throw new ArgumentException ("JNI Object Reference is invalid.", nameof (reference)); - } else { - newRef = reference; - - if ((options & JniObjectReferenceOptions.Copy) == JniObjectReferenceOptions.Copy) { - newRef = reference.NewGlobalRef (); - } - - JniObjectReference.Dispose (ref reference, options); - } - - peer.SetPeerReference (newRef); - peer.SetJniIdentityHashCode (JniSystem.IdentityHashCode (newRef)); - - var o = Runtime.ObjectReferenceManager; - if (o.LogGlobalReferenceMessages) { - o.WriteGlobalReferenceLine ("Created PeerReference={0} IdentityHashCode=0x{1} Instance=0x{2} Instance.Type={3}, Java.Type={4}", - newRef.ToString (), - peer.JniIdentityHashCode.ToString ("x"), - RuntimeHelpers.GetHashCode (peer).ToString ("x"), - peer.GetType ().FullName, - JniEnvironment.Types.GetJniTypeNameFromInstance (newRef)); - } - - if ((options & DoNotRegisterTarget) != DoNotRegisterTarget) { - AddPeer (peer); - } + ConstructPeerCore (peer, ref reference, options); } + protected abstract void ConstructPeerCore (IJavaPeerable peer, ref JniObjectReference reference, JniObjectReferenceOptions options); + public int GetJniIdentityHashCode (JniObjectReference reference) { return JniSystem.IdentityHashCode (reference); @@ -171,8 +103,7 @@ public int GetJniIdentityHashCode (JniObjectReference reference) public virtual void DisposePeer (IJavaPeerable value) { - if (disposed) - throw new ObjectDisposedException (GetType ().Name); + EnsureNotDisposed (); if (value == null) throw new ArgumentNullException (nameof (value)); @@ -192,8 +123,7 @@ public virtual void DisposePeer (IJavaPeerable value) void DisposePeer (JniObjectReference h, IJavaPeerable value) { - if (disposed) - throw new ObjectDisposedException (GetType ().Name); + EnsureNotDisposed (); var o = Runtime.ObjectReferenceManager; if (o.LogGlobalReferenceMessages) { @@ -204,14 +134,14 @@ void DisposePeer (JniObjectReference h, IJavaPeerable value) value.GetType ().ToString (), JniEnvironment.Types.GetJniTypeNameFromInstance (h)); } -#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES + #if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES var lref = value.PeerReference.SafeHandle as JniLocalReference; if (lref != null && !JniEnvironment.IsHandleValid (lref)) { // `lref` was created on another thread, and CANNOT be disposed on this thread. // Just invalidate the reference and move on. lref.SetHandleAsInvalid (); } -#endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES + #endif // FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES JniObjectReference.Dispose (ref h); value.SetPeerReference (new JniObjectReference ()); @@ -220,8 +150,7 @@ void DisposePeer (JniObjectReference h, IJavaPeerable value) public virtual void DisposePeerUnlessReferenced (IJavaPeerable value) { - if (disposed) - throw new ObjectDisposedException (GetType ().Name); + EnsureNotDisposed (); if (value == null) throw new ArgumentNullException (nameof (value)); @@ -237,12 +166,9 @@ public virtual void DisposePeerUnlessReferenced (IJavaPeerable value) DisposePeer (h, value); } - public abstract IJavaPeerable? PeekPeer (JniObjectReference reference); - public object? PeekValue (JniObjectReference reference) { - if (disposed) - throw new ObjectDisposedException (GetType ().Name); + EnsureNotDisposed (); if (!reference.IsValid) return null; @@ -273,37 +199,12 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr return false; } - object? PeekBoxedObject (JniObjectReference reference) - { - var t = PeekPeer (reference); - if (t == null) - return null; - object? r; - return TryUnboxPeerObject (t, out r) - ? r - : null; - } - - [return: DynamicallyAccessedMembers (Constructors)] - static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) - { - if (type == typeof (object)) - return typeof (JavaObject); - if (type == typeof (IJavaPeerable)) - return typeof (JavaObject); - if (type == typeof (Exception)) - return typeof (JavaException); - return type; - } - public IJavaPeerable? GetPeer ( JniObjectReference reference, [DynamicallyAccessedMembers (Constructors)] Type? targetType = null) { - if (disposed) { - throw new ObjectDisposedException (GetType ().Name); - } + EnsureNotDisposed (); if (!reference.IsValid) { return null; @@ -312,672 +213,105 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) var peeked = PeekPeer (reference); if (peeked != null && (targetType == null || - targetType.IsAssignableFrom (peeked.GetType ()))) { + targetType.IsAssignableFrom (peeked.GetType ()))) { return peeked; } return CreatePeer (ref reference, JniObjectReferenceOptions.Copy, targetType); } - // This base method implementation is NOT reachable in trimmable typemap - it is featureswitch guarded - public virtual IJavaPeerable? CreatePeer ( - ref JniObjectReference reference, - JniObjectReferenceOptions transfer, - [DynamicallyAccessedMembers (Constructors)] - Type? targetType) - { - if (disposed) - throw new ObjectDisposedException (GetType ().Name); - - if (!reference.IsValid) { - return null; - } - - targetType = targetType ?? typeof (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 ("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; - } - - 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) System.Runtime.CompilerServices.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; - } - - const BindingFlags ActivationConstructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; - 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 (ActivationConstructorBindingFlags, null, JIConstructorSignature, null); - if (c != null) { - var args = new object[] { - reference, - options, - }; - c.Invoke (self, args); - reference = (JniObjectReference) args [0]; - return true; - } - return false; - } + public abstract IJavaPeerable? CreatePeer ( + ref JniObjectReference reference, + JniObjectReferenceOptions transfer, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType); public object? CreateValue ( - ref JniObjectReference reference, - JniObjectReferenceOptions options, - [DynamicallyAccessedMembers (Constructors)] - Type? targetType = null) + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType = null) { - if (disposed) - throw new ObjectDisposedException (GetType ().Name); - - if (!reference.IsValid) - return null; - - if (targetType != null && typeof (IJavaPeerable).IsAssignableFrom (targetType)) { - return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); - } - - var boxed = PeekBoxedObject (reference); - if (boxed != null) { - JniObjectReference.Dispose (ref reference, options); - if (targetType != null) - return Convert.ChangeType (boxed, targetType); - return boxed; - } - - targetType = targetType ?? GetRuntimeType (reference); - if (targetType == null) { - // Let's hope this is an IJavaPeerable! - return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); - } - var marshaler = GetValueMarshaler (targetType); - return marshaler.CreateValue (ref reference, options, targetType); + return CreateValueCore (ref reference, options, targetType); } [return: MaybeNull] - public T CreateValue< - [DynamicallyAccessedMembers (Constructors)] - T - > ( - ref JniObjectReference reference, - JniObjectReferenceOptions options, - [DynamicallyAccessedMembers (Constructors)] - Type? targetType = null) + public T CreateValue<[DynamicallyAccessedMembers (Constructors)] T> ( + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType = null) { - if (disposed) - throw new ObjectDisposedException (GetType ().Name); - - if (!reference.IsValid) { -#pragma warning disable 8653 - return default (T); -#pragma warning restore 8653 - } - - if (targetType != null && !typeof (T).IsAssignableFrom (targetType)) - throw new ArgumentException ( - string.Format ("Requested runtime '{0}' value of '{1}' is not compatible with requested compile-time type T of '{2}'.", - nameof (targetType), - targetType, - typeof (T)), - nameof (targetType)); - - var boxed = PeekBoxedObject (reference); - if (boxed != null) { - JniObjectReference.Dispose (ref reference, options); - return (T) Convert.ChangeType (boxed, targetType ?? typeof (T)); - } - - targetType = targetType ?? typeof (T); - - if (typeof (IJavaPeerable).IsAssignableFrom (targetType)) { -#pragma warning disable CS8600,CS8601 // Possible null reference assignment. - return (T) JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); -#pragma warning restore CS8600,CS8601 // Possible null reference assignment. - } - - var marshaler = GetValueMarshaler (); - return marshaler.CreateGenericValue (ref reference, options, targetType); + return CreateValueCore (ref reference, options, targetType); } - [return: DynamicallyAccessedMembers (Constructors)] - internal Type? GetRuntimeType (JniObjectReference reference) - { - if (!reference.IsValid) - return null; - JniTypeSignature signature; - if (!JniTypeSignature.TryParse (JniEnvironment.Types.GetJniTypeNameFromInstance (reference)!, out signature)) - return null; - return Runtime.TypeManager.GetType (signature); - } + [return: MaybeNull] + protected abstract T CreateValueCore<[DynamicallyAccessedMembers (Constructors)] T> ( + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType = null); + + protected abstract object? CreateValueCore ( + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType = null); public object? GetValue ( - ref JniObjectReference reference, - JniObjectReferenceOptions options, - [DynamicallyAccessedMembers (Constructors)] - Type? targetType = null) + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType = null) { - if (disposed) - throw new ObjectDisposedException (GetType ().Name); - - if (!reference.IsValid) - return null; - - var existing = PeekValue (reference); - if (existing != null && (targetType == null || targetType.IsAssignableFrom (existing.GetType ()))) { - JniObjectReference.Dispose (ref reference, options); - return existing; - } - - if (targetType != null && typeof (IJavaPeerable).IsAssignableFrom (targetType)) { - return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); - } - - targetType = targetType ?? GetRuntimeType (reference); - if (targetType == null) { - // Let's hope this is an IJavaPeerable! - return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); - } - var marshaler = GetValueMarshaler (targetType); - return marshaler.CreateValue (ref reference, options, targetType); + return GetValueCore (ref reference, options, targetType); } [return: MaybeNull] - public T GetValue< - [DynamicallyAccessedMembers (Constructors)] - T - > ( - IntPtr handle) + public T GetValue<[DynamicallyAccessedMembers (Constructors)] T> (IntPtr handle) { var r = new JniObjectReference (handle); return GetValue (ref r, JniObjectReferenceOptions.Copy); } [return: MaybeNull] - public T GetValue< - [DynamicallyAccessedMembers (Constructors)] - T - > ( - ref JniObjectReference reference, - JniObjectReferenceOptions options, - [DynamicallyAccessedMembers (Constructors)] - Type? targetType = null) - { - if (!reference.IsValid) { -#pragma warning disable 8653 - return default (T); -#pragma warning restore 8653 - } - - if (targetType != null && !typeof (T).IsAssignableFrom (targetType)) - throw new ArgumentException ( - string.Format ("Requested runtime '{0}' value of '{1}' is not compatible with requested compile-time type T of '{2}'.", - nameof (targetType), - targetType, - typeof (T)), - nameof (targetType)); - - targetType = targetType ?? typeof (T); - - var existing = PeekValue (reference); - if (existing != null && (targetType == null || targetType.IsAssignableFrom (existing.GetType ()))) { - JniObjectReference.Dispose (ref reference, options); - return (T) existing; - } - - if (typeof (IJavaPeerable).IsAssignableFrom (targetType)) { -#pragma warning disable CS8600,CS8601 // Possible null reference assignment. - return (T) JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); -#pragma warning restore CS8600,CS8601 // Possible null reference assignment. - } - - var marshaler = GetValueMarshaler (); - return marshaler.CreateGenericValue (ref reference, options, targetType); - } - - Dictionary Marshalers = new Dictionary (); - - public JniValueMarshaler GetValueMarshaler< - [DynamicallyAccessedMembers (Constructors)] - T - > () - { - if (disposed) - throw new ObjectDisposedException (GetType ().Name); - - var m = GetValueMarshaler (typeof (T)); - var r = m as JniValueMarshaler; - if (r != null) - return r; - lock (Marshalers) { - if (!Marshalers.TryGetValue (typeof (T), out var d)) - Marshalers.Add (typeof (T), d = new DelegatingValueMarshaler (m)); - return (JniValueMarshaler) d; - } - } - - public JniValueMarshaler GetValueMarshaler (Type type) - { - if (disposed) - throw new ObjectDisposedException (GetType ().Name); - - if (type == null) - throw new ArgumentNullException (nameof (type)); - if (type.ContainsGenericParameters) - throw new ArgumentException ("Generic type definitions are not supported.", nameof (type)); - - var marshalerAttr = type.GetCustomAttribute (); - if (marshalerAttr != null) - return (JniValueMarshaler) Activator.CreateInstance (marshalerAttr.MarshalerType)!; - - if (typeof (IJavaPeerable) == type) - return JavaPeerableValueMarshaler.Instance; - - if (typeof (void) == type) - return VoidValueMarshaler.Instance; - - foreach (var marshaler in JniBuiltinMarshalers.Value) { - if (marshaler.Key == type) - return marshaler.Value; - } - - - var listType = GetListType (type); - if (listType != null) { - var elementType = listType.GenericTypeArguments [0]; - if (elementType.IsValueType) { - foreach (var marshaler in JniPrimitiveArrayMarshalers.Value) { - if (type.IsAssignableFrom (marshaler.Key)) - return marshaler.Value; - } - } - - return GetObjectArrayMarshaler (elementType); - } - - if (typeof (IJavaPeerable).IsAssignableFrom (type)) { - return JavaPeerableValueMarshaler.Instance; - } - - return GetValueMarshalerCore (type); - } - - static Type? GetListType(Type type) - { - foreach (var iface in GetInterfaces (type).Concat (new [] { type })) { - if (typeof (IList<>).IsAssignableFrom (iface.IsGenericType ? iface.GetGenericTypeDefinition () : iface)) - return iface; - } - return null; - - [UnconditionalSuppressMessage ("Trimming", "IL2070", Justification = "We handle the case if IList<> is trimmed away")] - static Type [] GetInterfaces (Type type) => - type.GetInterfaces (); - } - - static JniValueMarshaler GetObjectArrayMarshaler (Type elementType) - { - const string makeGenericMethodMessage = "This code path is not used in Android projects."; - - // FIXME: https://github.com/dotnet/java-interop/issues/1192 - [UnconditionalSuppressMessage ("Trimming", "IL2060", Justification = makeGenericMethodMessage)] - [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = makeGenericMethodMessage)] - static MethodInfo MakeGenericMethod (MethodInfo method, Type type) => - method.MakeGenericMethod (type); - - Func indirect = GetObjectArrayMarshalerHelper; - var reifiedMethodInfo = MakeGenericMethod ( - indirect.Method.GetGenericMethodDefinition (), elementType); - Func direct = (Func) Delegate.CreateDelegate (typeof (Func), reifiedMethodInfo); - return direct (); - } - - static JniValueMarshaler GetObjectArrayMarshalerHelper< - [DynamicallyAccessedMembers (Constructors)] - T - > () - { - return JavaObjectArray.Instance; - } - - protected virtual JniValueMarshaler GetValueMarshalerCore (Type type) - { - return ProxyValueMarshaler.Instance; - } - } - } - - sealed class VoidValueMarshaler : JniValueMarshaler { - - internal static VoidValueMarshaler Instance = new VoidValueMarshaler (); - - public override Type MarshalType { - get {return typeof (void);} - } - - public override object? CreateValue ( - ref JniObjectReference reference, - JniObjectReferenceOptions options, - [DynamicallyAccessedMembers (Constructors)] - Type? targetType) - { - throw new NotSupportedException (); - } - - public override JniValueMarshalerState CreateObjectReferenceArgumentState (object? value, ParameterAttributes synchronize) - { - throw new NotSupportedException (); - } - - public override void DestroyArgumentState (object? value, ref JniValueMarshalerState state, ParameterAttributes synchronize) - { - throw new NotSupportedException (); - } - } - - sealed class JavaPeerableValueMarshaler : JniValueMarshaler { - - internal static JavaPeerableValueMarshaler Instance = new JavaPeerableValueMarshaler (); - - [return: MaybeNull] - public override IJavaPeerable? CreateGenericValue ( + public T GetValue<[DynamicallyAccessedMembers (Constructors)] T> ( ref JniObjectReference reference, JniObjectReferenceOptions options, [DynamicallyAccessedMembers (Constructors)] - Type? targetType) - { - var jvm = JniEnvironment.Runtime; - var marshaler = jvm.ValueManager.GetValueMarshaler (targetType ?? typeof(IJavaPeerable)); - if (marshaler != Instance) - return (IJavaPeerable) marshaler.CreateValue (ref reference, options, targetType)!; - return jvm.ValueManager.CreatePeer (ref reference, options, targetType); - } - - public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull]IJavaPeerable? value, ParameterAttributes synchronize) - { - if (value == null || !value.PeerReference.IsValid) - return new JniValueMarshalerState (); - var r = value.PeerReference.NewLocalRef (); - return new JniValueMarshalerState (r); - } - - public override void DestroyGenericArgumentState ([MaybeNull]IJavaPeerable? value, ref JniValueMarshalerState state, ParameterAttributes synchronize) - { - var r = state.ReferenceValue; - JniObjectReference.Dispose (ref r); - state = new JniValueMarshalerState (); - } - - [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] - public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) - { - var r = CreateIntermediaryExpressionFromManagedExpression (context, sourceValue); - var h = Expression.Variable (typeof (IntPtr), sourceValue.Name + "_handle"); - context.LocalVariables.Add (h); - context.CreationStatements.Add (Expression.Assign (h, Expression.Property (r, "Handle"))); - - return h; - } - - [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] - Expression CreateIntermediaryExpressionFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) - { - var r = Expression.Variable (typeof (JniObjectReference), sourceValue.Name + "_ref"); - context.LocalVariables.Add (r); - context.CreationStatements.Add ( - Expression.IfThenElse ( - test: Expression.Equal (Expression.Constant (null), sourceValue), - ifTrue: Expression.Assign (r, Expression.New (typeof (JniObjectReference))), - ifFalse: Expression.Assign (r, Expression.Property (Expression.Convert (sourceValue, typeof (IJavaPeerable)), "PeerReference")))); - - return r; - } - - [RequiresDynamicCode (ExpressionRequiresUnreferencedCode)] - [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] - public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) - { - return ReturnObjectReferenceToJni (context, sourceValue.Name, CreateIntermediaryExpressionFromManagedExpression (context, sourceValue)); - } - - [RequiresDynamicCode (ExpressionRequiresUnreferencedCode)] - [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] - public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type? targetType) - { - targetType ??= typeof (object); - - var r = Expression.Variable (targetType, sourceValue.Name + "_val"); - context.LocalVariables.Add (r); - context.CreationStatements.Add ( - Expression.Assign (r, - Expression.Call ( - context.ValueManager ?? Expression.Property (context.Runtime, "ValueManager"), - "GetValue", - new[]{targetType}, - sourceValue))); - return r; - } - } - - sealed class DelegatingValueMarshaler< - [DynamicallyAccessedMembers (Constructors)] - T - > - : JniValueMarshaler - { - - JniValueMarshaler ValueMarshaler; - - public DelegatingValueMarshaler (JniValueMarshaler valueMarshaler) - { - ValueMarshaler = valueMarshaler; - } + Type? targetType = null) + { + return GetValueCore (ref reference, options, targetType); + } - [return: MaybeNull] - public override T CreateGenericValue ( + [return: MaybeNull] + protected abstract T GetValueCore<[DynamicallyAccessedMembers (Constructors)] T> ( ref JniObjectReference reference, JniObjectReferenceOptions options, [DynamicallyAccessedMembers (Constructors)] - Type? targetType) - { - return (T) ValueMarshaler.CreateValue (ref reference, options, targetType ?? typeof (T))!; - } + Type? targetType = null); - public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull]T value, ParameterAttributes synchronize) - { - return ValueMarshaler.CreateObjectReferenceArgumentState (value, synchronize); - } - - public override void DestroyGenericArgumentState ([AllowNull]T value, ref JniValueMarshalerState state, ParameterAttributes synchronize) - { - ValueMarshaler.DestroyArgumentState (value, ref state, synchronize); - } - - [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] - public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) - { - return ValueMarshaler.CreateParameterFromManagedExpression (context, sourceValue, synchronize); - } - - [RequiresDynamicCode (ExpressionRequiresUnreferencedCode)] - [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] - public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type? targetType) - { - return ValueMarshaler.CreateParameterToManagedExpression (context, sourceValue, synchronize, targetType); - } - - [RequiresDynamicCode (ExpressionRequiresUnreferencedCode)] - [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] - public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) - { - return ValueMarshaler.CreateReturnValueFromManagedExpression (context, sourceValue); - } - } - - sealed class ProxyValueMarshaler : JniValueMarshaler { - - internal static ProxyValueMarshaler Instance = new ProxyValueMarshaler (); - - [return: MaybeNull] - public override object? CreateGenericValue ( + protected abstract object? GetValueCore ( ref JniObjectReference reference, JniObjectReferenceOptions options, [DynamicallyAccessedMembers (Constructors)] - Type? targetType) - { - var jvm = JniEnvironment.Runtime; + Type? targetType = null); - if (targetType == null || targetType == typeof (object)) { - targetType = jvm.ValueManager.GetRuntimeType (reference); - } - if (targetType != null) { - var vm = jvm.ValueManager.GetValueMarshaler (targetType); - if (vm != Instance) { - return vm.CreateValue (ref reference, options, targetType)!; - } - } - - var target = jvm.ValueManager.PeekValue (reference); - if (target != null) { - JniObjectReference.Dispose (ref reference, options); - return target; - } - // Punt! Hope it's a java.lang.Object - return jvm.ValueManager.CreatePeer (ref reference, options, targetType); - } - - public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull]object? value, ParameterAttributes synchronize) - { - if (value == null) - return new JniValueMarshalerState (); - - var jvm = JniEnvironment.Runtime; - - var vm = jvm.ValueManager.GetValueMarshaler (value.GetType ()); - if (vm != Instance) { - var s = vm.CreateObjectReferenceArgumentState (value, synchronize); - return new JniValueMarshalerState (s, vm); + [return: DynamicallyAccessedMembers (Constructors)] + internal Type? GetRuntimeType (JniObjectReference reference) + { + if (!reference.IsValid) + return null; + JniTypeSignature signature; + if (!JniTypeSignature.TryParse (JniEnvironment.Types.GetJniTypeNameFromInstance (reference)!, out signature)) + return null; + return Runtime.TypeManager.GetType (signature); } - var p = JavaProxyObject.GetProxy (value); - return new JniValueMarshalerState (p!.PeerReference.NewLocalRef ()); - } + public JniValueMarshaler GetValueMarshaler (Type type) => GetValueMarshalerCore (type); + protected abstract JniValueMarshaler GetValueMarshalerCore (Type type); - public override void DestroyGenericArgumentState (object? value, ref JniValueMarshalerState state, ParameterAttributes synchronize) - { - var vm = state.Extra as JniValueMarshaler; - if (vm != null) { - vm.DestroyArgumentState (value, ref state, synchronize); - return; - } - var r = state.ReferenceValue; - JniObjectReference.Dispose (ref r); - state = new JniValueMarshalerState (); + public JniValueMarshaler GetValueMarshaler<[DynamicallyAccessedMembers (Constructors)] T> () => GetValueMarshalerCore (); + protected abstract JniValueMarshaler GetValueMarshalerCore<[DynamicallyAccessedMembers (Constructors)] T> (); } } } diff --git a/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs new file mode 100644 index 000000000..0b3d8e4f1 --- /dev/null +++ b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs @@ -0,0 +1,720 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; + +using Java.Interop.Expressions; + +namespace Java.Interop +{ + partial class JniRuntime + { + [RequiresDynamicCode ("TODO")] + [RequiresUnreferencedCode ("TODO")] + public abstract partial class ReflectionJniValueManager : JniValueManager + { + public override void ActivatePeer ( + JniObjectReference reference, + [DynamicallyAccessedMembers (Constructors)] Type type, + ConstructorInfo cinfo, + object?[]? argumentValues) + { + try { + var self = (IJavaPeerable) RuntimeHelpers.GetUninitializedObject (type); + self.SetPeerReference (reference); + cinfo.Invoke (self, argumentValues); + } catch (Exception e) { + var m = string.Format ( + CultureInfo.InvariantCulture, + "Could not activate {{ PeerReference={0} IdentityHashCode=0x{1} Java.Type={2} }} for managed type '{3}'.", + reference, + GetJniIdentityHashCode (reference).ToString ("x", CultureInfo.InvariantCulture), + JniEnvironment.Types.GetJniTypeNameFromInstance (reference), + type.FullName); + Debug.WriteLine (m); + + throw new NotSupportedException (m, e); + } + } + + protected override void ConstructPeerCore (IJavaPeerable peer, ref JniObjectReference reference, JniObjectReferenceOptions options) + { + if (peer == null) + throw new ArgumentNullException (nameof (peer)); + + var newRef = peer.PeerReference; + if (newRef.IsValid) { + JniObjectReference.Dispose (ref reference, options); + + // Activation? See ManagedPeer.Construct, CreatePeer + // Instance was already added, don't add again + if (peer.JniManagedPeerState.HasFlag (JniManagedPeerStates.Activatable)) { + return; + } + var orig = newRef; + newRef = orig.NewGlobalRef (); + JniObjectReference.Dispose (ref orig); + } else if (options == JniObjectReferenceOptions.None) { + // `reference` is likely *InvalidJniObjectReference, and can't be touched + return; + } else if (!reference.IsValid) { + throw new ArgumentException ("JNI Object Reference is invalid.", nameof (reference)); + } else { + newRef = reference; + + if ((options & JniObjectReferenceOptions.Copy) == JniObjectReferenceOptions.Copy) { + newRef = reference.NewGlobalRef (); + } + + JniObjectReference.Dispose (ref reference, options); + } + + peer.SetPeerReference (newRef); + peer.SetJniIdentityHashCode (JniSystem.IdentityHashCode (newRef)); + + var o = Runtime.ObjectReferenceManager; + if (o.LogGlobalReferenceMessages) { + o.WriteGlobalReferenceLine ("Created PeerReference={0} IdentityHashCode=0x{1} Instance=0x{2} Instance.Type={3}, Java.Type={4}", + newRef.ToString (), + peer.JniIdentityHashCode.ToString ("x"), + RuntimeHelpers.GetHashCode (peer).ToString ("x"), + peer.GetType ().FullName, + JniEnvironment.Types.GetJniTypeNameFromInstance (newRef)); + } + + if ((options & DoNotRegisterTarget) != DoNotRegisterTarget) { + AddPeer (peer); + } + } + + // This base method implementation is NOT reachable in trimmable typemap - it is featureswitch guarded + public override IJavaPeerable? CreatePeer ( + ref JniObjectReference reference, + JniObjectReferenceOptions transfer, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType) + { + EnsureNotDisposed (); + + if (!reference.IsValid) { + return null; + } + + targetType = targetType ?? typeof (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 ("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 (JavaObject); + if (type == typeof (IJavaPeerable)) + return typeof (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) System.Runtime.CompilerServices.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; + } + + const BindingFlags ActivationConstructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + static readonly Type ByRefJniObjectReference = typeof (JniObjectReference).MakeByRefType (); + static readonly Type[] JIConstructorSignature = new Type [] { ByRefJniObjectReference, typeof (JniObjectReferenceOptions) }; + + bool TryConstructPeer ( + IJavaPeerable self, + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type type) + { + var c = type.GetConstructor (ActivationConstructorBindingFlags, null, JIConstructorSignature, null); + if (c != null) { + var args = new object[] { + reference, + options, + }; + c.Invoke (self, args); + reference = (JniObjectReference) args [0]; + return true; + } + return false; + } + + protected override object? CreateValueCore ( + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType = null) + { + EnsureNotDisposed (); + + if (!reference.IsValid) + return null; + + if (targetType != null && typeof (IJavaPeerable).IsAssignableFrom (targetType)) { + return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); + } + + var boxed = PeekBoxedObject (reference); + if (boxed != null) { + JniObjectReference.Dispose (ref reference, options); + if (targetType != null) + return Convert.ChangeType (boxed, targetType); + return boxed; + } + + targetType = targetType ?? GetRuntimeType (reference); + if (targetType == null) { + // Let's hope this is an IJavaPeerable! + return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); + } + var marshaler = GetValueMarshaler (targetType); + return marshaler.CreateValue (ref reference, options, targetType); + } + + [return: MaybeNull] + protected override T CreateValueCore<[DynamicallyAccessedMembers (Constructors)] T> ( + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType = null) + { + EnsureNotDisposed (); + + if (!reference.IsValid) { + #pragma warning disable 8653 + return default (T); + #pragma warning restore 8653 + } + + if (targetType != null && !typeof (T).IsAssignableFrom (targetType)) + throw new ArgumentException ( + string.Format ("Requested runtime '{0}' value of '{1}' is not compatible with requested compile-time type T of '{2}'.", + nameof (targetType), + targetType, + typeof (T)), + nameof (targetType)); + + var boxed = PeekBoxedObject (reference); + if (boxed != null) { + JniObjectReference.Dispose (ref reference, options); + return (T) Convert.ChangeType (boxed, targetType ?? typeof (T)); + } + + targetType = targetType ?? typeof (T); + + if (typeof (IJavaPeerable).IsAssignableFrom (targetType)) { + #pragma warning disable CS8600,CS8601 // Possible null reference assignment. + return (T) JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); + #pragma warning restore CS8600,CS8601 // Possible null reference assignment. + } + + var marshaler = GetValueMarshaler (); + return marshaler.CreateGenericValue (ref reference, options, targetType); + } + + object? PeekBoxedObject (JniObjectReference reference) + { + var t = PeekPeer (reference); + if (t == null) + return null; + object? r; + return TryUnboxPeerObject (t, out r) + ? r + : null; + } + + protected override object? GetValueCore ( + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType = null) + { + EnsureNotDisposed (); + + if (!reference.IsValid) + return null; + + var existing = PeekValue (reference); + if (existing != null && (targetType == null || targetType.IsAssignableFrom (existing.GetType ()))) { + JniObjectReference.Dispose (ref reference, options); + return existing; + } + + if (targetType != null && typeof (IJavaPeerable).IsAssignableFrom (targetType)) { + return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); + } + + targetType = targetType ?? GetRuntimeType (reference); + if (targetType == null) { + // Let's hope this is an IJavaPeerable! + return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); + } + var marshaler = GetValueMarshaler (targetType); + return marshaler.CreateValue (ref reference, options, targetType); + } + + + [return: MaybeNull] + protected override T GetValueCore<[DynamicallyAccessedMembers (Constructors)] T> ( + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType = null) + { + if (!reference.IsValid) { + #pragma warning disable 8653 + return default (T); + #pragma warning restore 8653 + } + + if (targetType != null && !typeof (T).IsAssignableFrom (targetType)) + throw new ArgumentException ( + string.Format ("Requested runtime '{0}' value of '{1}' is not compatible with requested compile-time type T of '{2}'.", + nameof (targetType), + targetType, + typeof (T)), + nameof (targetType)); + + targetType = targetType ?? typeof (T); + + var existing = PeekValue (reference); + if (existing != null && (targetType == null || targetType.IsAssignableFrom (existing.GetType ()))) { + JniObjectReference.Dispose (ref reference, options); + return (T) existing; + } + + if (typeof (IJavaPeerable).IsAssignableFrom (targetType)) { + #pragma warning disable CS8600,CS8601 // Possible null reference assignment. + return (T) JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); + #pragma warning restore CS8600,CS8601 // Possible null reference assignment. + } + + var marshaler = GetValueMarshaler (); + return marshaler.CreateGenericValue (ref reference, options, targetType); + } + + Dictionary Marshalers = new Dictionary (); + + protected override JniValueMarshaler GetValueMarshalerCore<[DynamicallyAccessedMembers (Constructors)] T> () + { + EnsureNotDisposed (); + + var m = GetValueMarshaler (typeof (T)); + var r = m as JniValueMarshaler; + if (r != null) + return r; + lock (Marshalers) { + if (!Marshalers.TryGetValue (typeof (T), out var d)) + Marshalers.Add (typeof (T), d = new DelegatingValueMarshaler (m)); + return (JniValueMarshaler) d; + } + } + + protected override JniValueMarshaler GetValueMarshalerCore (Type type) + { + EnsureNotDisposed (); + + if (type == null) + throw new ArgumentNullException (nameof (type)); + if (type.ContainsGenericParameters) + throw new ArgumentException ("Generic type definitions are not supported.", nameof (type)); + + var marshalerAttr = type.GetCustomAttribute (); + if (marshalerAttr != null) + return (JniValueMarshaler) Activator.CreateInstance (marshalerAttr.MarshalerType)!; + + if (typeof (IJavaPeerable) == type) + return JavaPeerableValueMarshaler.Instance; + + if (typeof (void) == type) + return VoidValueMarshaler.Instance; + + foreach (var marshaler in JniBuiltinMarshalers.Value) { + if (marshaler.Key == type) + return marshaler.Value; + } + + + var listType = GetListType (type); + if (listType != null) { + var elementType = listType.GenericTypeArguments [0]; + if (elementType.IsValueType) { + foreach (var marshaler in JniPrimitiveArrayMarshalers.Value) { + if (type.IsAssignableFrom (marshaler.Key)) + return marshaler.Value; + } + } + + return GetObjectArrayMarshaler (elementType); + } + + if (typeof (IJavaPeerable).IsAssignableFrom (type)) { + return JavaPeerableValueMarshaler.Instance; + } + + return ProxyValueMarshaler.Instance; + } + + static Type? GetListType(Type type) + { + foreach (var iface in type.GetInterfaces ().Concat (new [] { type })) { + if (typeof (IList<>).IsAssignableFrom (iface.IsGenericType ? iface.GetGenericTypeDefinition () : iface)) + return iface; + } + return null; + } + + static JniValueMarshaler GetObjectArrayMarshaler (Type elementType) + { + Func indirect = static () => JavaObjectArray.Instance; + var reifiedMethodInfo = indirect.Method.GetGenericMethodDefinition ().MakeGenericMethod (elementType); + Func direct = (Func) Delegate.CreateDelegate (typeof (Func), reifiedMethodInfo); + return direct (); + } + + } + + sealed class VoidValueMarshaler : JniValueMarshaler { + + internal static VoidValueMarshaler Instance = new VoidValueMarshaler (); + + public override Type MarshalType { + get {return typeof (void);} + } + + public override object? CreateValue ( + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType) + { + throw new NotSupportedException (); + } + + public override JniValueMarshalerState CreateObjectReferenceArgumentState (object? value, ParameterAttributes synchronize) + { + throw new NotSupportedException (); + } + + public override void DestroyArgumentState (object? value, ref JniValueMarshalerState state, ParameterAttributes synchronize) + { + throw new NotSupportedException (); + } + } + } + + sealed class JavaPeerableValueMarshaler : JniValueMarshaler { + + internal static JavaPeerableValueMarshaler Instance = new JavaPeerableValueMarshaler (); + + [return: MaybeNull] + public override IJavaPeerable? CreateGenericValue ( + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType) + { + var jvm = JniEnvironment.Runtime; + var marshaler = jvm.ValueManager.GetValueMarshaler (targetType ?? typeof(IJavaPeerable)); + if (marshaler != Instance) + return (IJavaPeerable) marshaler.CreateValue (ref reference, options, targetType)!; + return jvm.ValueManager.CreatePeer (ref reference, options, targetType); + } + + public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull]IJavaPeerable? value, ParameterAttributes synchronize) + { + if (value == null || !value.PeerReference.IsValid) + return new JniValueMarshalerState (); + var r = value.PeerReference.NewLocalRef (); + return new JniValueMarshalerState (r); + } + + public override void DestroyGenericArgumentState ([MaybeNull]IJavaPeerable? value, ref JniValueMarshalerState state, ParameterAttributes synchronize) + { + var r = state.ReferenceValue; + JniObjectReference.Dispose (ref r); + state = new JniValueMarshalerState (); + } + + [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] + public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) + { + var r = CreateIntermediaryExpressionFromManagedExpression (context, sourceValue); + var h = Expression.Variable (typeof (IntPtr), sourceValue.Name + "_handle"); + context.LocalVariables.Add (h); + context.CreationStatements.Add (Expression.Assign (h, Expression.Property (r, "Handle"))); + + return h; + } + + [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] + Expression CreateIntermediaryExpressionFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) + { + var r = Expression.Variable (typeof (JniObjectReference), sourceValue.Name + "_ref"); + context.LocalVariables.Add (r); + context.CreationStatements.Add ( + Expression.IfThenElse ( + test: Expression.Equal (Expression.Constant (null), sourceValue), + ifTrue: Expression.Assign (r, Expression.New (typeof (JniObjectReference))), + ifFalse: Expression.Assign (r, Expression.Property (Expression.Convert (sourceValue, typeof (IJavaPeerable)), "PeerReference")))); + + return r; + } + + [RequiresDynamicCode (ExpressionRequiresUnreferencedCode)] + [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] + public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) + { + return ReturnObjectReferenceToJni (context, sourceValue.Name, CreateIntermediaryExpressionFromManagedExpression (context, sourceValue)); + } + + [RequiresDynamicCode (ExpressionRequiresUnreferencedCode)] + [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] + public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type? targetType) + { + targetType ??= typeof (object); + + var r = Expression.Variable (targetType, sourceValue.Name + "_val"); + context.LocalVariables.Add (r); + context.CreationStatements.Add ( + Expression.Assign (r, + Expression.Call ( + context.ValueManager ?? Expression.Property (context.Runtime, "ValueManager"), + "GetValue", + new[]{targetType}, + sourceValue))); + return r; + } + } + + sealed class DelegatingValueMarshaler< + [DynamicallyAccessedMembers (Constructors)] + T + > + : JniValueMarshaler + { + + JniValueMarshaler ValueMarshaler; + + public DelegatingValueMarshaler (JniValueMarshaler valueMarshaler) + { + ValueMarshaler = valueMarshaler; + } + + [return: MaybeNull] + public override T CreateGenericValue ( + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType) + { + return (T) ValueMarshaler.CreateValue (ref reference, options, targetType ?? typeof (T))!; + } + + public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull]T value, ParameterAttributes synchronize) + { + return ValueMarshaler.CreateObjectReferenceArgumentState (value, synchronize); + } + + public override void DestroyGenericArgumentState ([AllowNull]T value, ref JniValueMarshalerState state, ParameterAttributes synchronize) + { + ValueMarshaler.DestroyArgumentState (value, ref state, synchronize); + } + + [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] + public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) + { + return ValueMarshaler.CreateParameterFromManagedExpression (context, sourceValue, synchronize); + } + + [RequiresDynamicCode (ExpressionRequiresUnreferencedCode)] + [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] + public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type? targetType) + { + return ValueMarshaler.CreateParameterToManagedExpression (context, sourceValue, synchronize, targetType); + } + + [RequiresDynamicCode (ExpressionRequiresUnreferencedCode)] + [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] + public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue) + { + return ValueMarshaler.CreateReturnValueFromManagedExpression (context, sourceValue); + } + } + + sealed class ProxyValueMarshaler : JniValueMarshaler { + + internal static ProxyValueMarshaler Instance = new ProxyValueMarshaler (); + + [return: MaybeNull] + public override object? CreateGenericValue ( + ref JniObjectReference reference, + JniObjectReferenceOptions options, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType) + { + var jvm = JniEnvironment.Runtime; + + if (targetType == null || targetType == typeof (object)) { + targetType = jvm.ValueManager.GetRuntimeType (reference); + } + if (targetType != null) { + var vm = jvm.ValueManager.GetValueMarshaler (targetType); + if (vm != Instance) { + return vm.CreateValue (ref reference, options, targetType)!; + } + } + + var target = jvm.ValueManager.PeekValue (reference); + if (target != null) { + JniObjectReference.Dispose (ref reference, options); + return target; + } + // Punt! Hope it's a java.lang.Object + return jvm.ValueManager.CreatePeer (ref reference, options, targetType); + } + + public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull]object? value, ParameterAttributes synchronize) + { + if (value == null) + return new JniValueMarshalerState (); + + var jvm = JniEnvironment.Runtime; + + var vm = jvm.ValueManager.GetValueMarshaler (value.GetType ()); + if (vm != Instance) { + var s = vm.CreateObjectReferenceArgumentState (value, synchronize); + return new JniValueMarshalerState (s, vm); + } + + var p = JavaProxyObject.GetProxy (value); + return new JniValueMarshalerState (p!.PeerReference.NewLocalRef ()); + } + + public override void DestroyGenericArgumentState (object? value, ref JniValueMarshalerState state, ParameterAttributes synchronize) + { + var vm = state.Extra as JniValueMarshaler; + if (vm != null) { + vm.DestroyArgumentState (value, ref state, synchronize); + return; + } + var r = state.ReferenceValue; + JniObjectReference.Dispose (ref r); + state = new JniValueMarshalerState (); + } + } +} diff --git a/src/Java.Interop/PublicAPI.Shipped.txt b/src/Java.Interop/PublicAPI.Shipped.txt index bd748c279..b03076199 100644 --- a/src/Java.Interop/PublicAPI.Shipped.txt +++ b/src/Java.Interop/PublicAPI.Shipped.txt @@ -907,11 +907,11 @@ abstract Java.Interop.JniRuntime.JniTypeManager.GetTypesForSimpleReference(strin virtual Java.Interop.JniRuntime.JniTypeManager.OnSetRuntime(Java.Interop.JniRuntime! runtime) -> 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? +abstract 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 virtual Java.Interop.JniRuntime.JniValueManager.DisposePeerUnlessReferenced(Java.Interop.IJavaPeerable! value) -> void -virtual Java.Interop.JniRuntime.JniValueManager.GetValueMarshalerCore(System.Type! type) -> Java.Interop.JniValueMarshaler! +abstract Java.Interop.JniRuntime.JniValueManager.GetValueMarshalerCore(System.Type! type) -> Java.Interop.JniValueMarshaler! virtual Java.Interop.JniRuntime.JniValueManager.OnSetRuntime(Java.Interop.JniRuntime! runtime) -> void virtual Java.Interop.JniRuntime.JniValueManager.TryUnboxPeerObject(Java.Interop.IJavaPeerable! value, out object? result) -> bool virtual Java.Interop.JniRuntime.RaisePendingException(System.Exception! pendingException) -> void diff --git a/src/Java.Interop/PublicAPI.Unshipped.txt b/src/Java.Interop/PublicAPI.Unshipped.txt index 49a9dc38e..7dc728c0c 100644 --- a/src/Java.Interop/PublicAPI.Unshipped.txt +++ b/src/Java.Interop/PublicAPI.Unshipped.txt @@ -8,7 +8,6 @@ abstract Java.Interop.JniRuntime.JniTypeManager.GetReflectionConstructibleTypes( 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.ReflectionJniTypeManager @@ -20,6 +19,9 @@ 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.JniRuntime.JniValueManager.EnsureNotDisposed() -> void +Java.Interop.JniRuntime.ReflectionJniValueManager +Java.Interop.JniRuntime.ReflectionJniValueManager.ReflectionJniValueManager() -> void Java.Interop.JniTypeSignatureAttribute.InvokerType.get -> System.Type? Java.Interop.JniTypeSignatureAttribute.InvokerType.set -> void Java.Interop.IJavaPeerable.JniObjectReferenceControlBlock.get -> nint @@ -46,7 +48,22 @@ static Java.Interop.JniEnvironment.InstanceMethods.GetMethodID(Java.Interop.JniO static Java.Interop.JniEnvironment.StaticFields.GetStaticFieldID(Java.Interop.JniObjectReference type, System.ReadOnlySpan name, System.ReadOnlySpan signature) -> Java.Interop.JniFieldInfo! 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 +abstract Java.Interop.JniRuntime.JniValueManager.ActivatePeer(Java.Interop.JniObjectReference reference, System.Type! type, System.Reflection.ConstructorInfo! cinfo, object?[]? argumentValues) -> void +abstract Java.Interop.JniRuntime.JniValueManager.ConstructPeerCore(Java.Interop.IJavaPeerable! peer, ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options) -> void +abstract Java.Interop.JniRuntime.JniValueManager.CreateValueCore(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type? targetType = null) -> object? +abstract Java.Interop.JniRuntime.JniValueManager.CreateValueCore(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type? targetType = null) -> T +abstract Java.Interop.JniRuntime.JniValueManager.GetValueCore(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type? targetType = null) -> object? +abstract Java.Interop.JniRuntime.JniValueManager.GetValueCore(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type? targetType = null) -> T +abstract Java.Interop.JniRuntime.JniValueManager.GetValueMarshalerCore() -> Java.Interop.JniValueMarshaler! +override Java.Interop.JniRuntime.ReflectionJniValueManager.ActivatePeer(Java.Interop.JniObjectReference reference, System.Type! type, System.Reflection.ConstructorInfo! cinfo, object?[]? argumentValues) -> void +override Java.Interop.JniRuntime.ReflectionJniValueManager.ConstructPeerCore(Java.Interop.IJavaPeerable! peer, ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options) -> void +override Java.Interop.JniRuntime.ReflectionJniValueManager.CreatePeer(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions transfer, System.Type? targetType) -> Java.Interop.IJavaPeerable? +override Java.Interop.JniRuntime.ReflectionJniValueManager.CreateValueCore(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type? targetType = null) -> object? +override Java.Interop.JniRuntime.ReflectionJniValueManager.CreateValueCore(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type? targetType = null) -> T +override Java.Interop.JniRuntime.ReflectionJniValueManager.GetValueCore(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type? targetType = null) -> object? +override Java.Interop.JniRuntime.ReflectionJniValueManager.GetValueCore(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type? targetType = null) -> T +override Java.Interop.JniRuntime.ReflectionJniValueManager.GetValueMarshalerCore(System.Type! type) -> Java.Interop.JniValueMarshaler! +override Java.Interop.JniRuntime.ReflectionJniValueManager.GetValueMarshalerCore() -> Java.Interop.JniValueMarshaler! override Java.Interop.JniRuntime.ReflectionJniTypeManager.GetInvokerTypeCore(System.Type! type) -> System.Type? override Java.Interop.JniRuntime.ReflectionJniTypeManager.GetReflectionConstructibleTypes(Java.Interop.JniTypeSignature typeSignature) -> System.Collections.Generic.IEnumerable! override Java.Interop.JniRuntime.ReflectionJniTypeManager.GetReplacementMethodInfoCore(string! jniSimpleReference, string! jniMethodName, string! jniMethodSignature) -> Java.Interop.JniRuntime.ReplacementMethodInfo? From 65e6c60111accb8c6ae554d7468ccf37d1bed907 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 9 Jun 2026 11:44:55 +0200 Subject: [PATCH 18/28] Undo unnecessary changes --- src/Java.Interop/Java.Interop/ManagedPeer.cs | 27 ++------------------ 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/src/Java.Interop/Java.Interop/ManagedPeer.cs b/src/Java.Interop/Java.Interop/ManagedPeer.cs index a3efd68fb..5fffb81a6 100644 --- a/src/Java.Interop/Java.Interop/ManagedPeer.cs +++ b/src/Java.Interop/Java.Interop/ManagedPeer.cs @@ -79,19 +79,8 @@ static void Construct ( var runtime = JniEnvironment.Runtime; var r_self = new JniObjectReference (n_self); var self = runtime.ValueManager.PeekPeer (r_self); - if (self != null) { var state = self.JniManagedPeerState; - if ((state & JniManagedPeerStates.Activatable) != JniManagedPeerStates.Activatable && - (state & JniManagedPeerStates.Replaceable) != JniManagedPeerStates.Replaceable) { - return; - } - } - - if (JniEnvironment.WithinNewObjectScope) { - if (runtime.ObjectReferenceManager.LogGlobalReferenceMessages) { - runtime.ObjectReferenceManager.WriteGlobalReferenceLine ( - "Warning: Skipping managed constructor invocation for PeerReference={0} IdentityHashCode=0x{1} Java.Type={2}. " + - "Please use JniPeerMembers.InstanceMethods.StartCreateInstance() + JniPeerMembers.InstanceMethods.FinishCreateInstance() instead of " + + JniEnvironment.Runtime.TypeManager.RegisterNativeMembers (nativeClass, typeSig, methods); "JniEnvironment.Object.NewObject().", r_self, runtime.ValueManager.GetJniIdentityHashCode (r_self).ToString ("x"), @@ -225,11 +214,6 @@ static List[] GetConstructorCandidateParameterTypes (string signature) int i = 0; foreach (var jniType in JniMemberSignature.GetParameterTypesFromMethodSignature (signature)) { var possibleTypes = new List (typeManager.GetTypes (jniType)); - if (possibleTypes.Count == 0 && jniType.ArrayRank == 0) { - var type = typeManager.GetType (jniType); - if (type != null) - possibleTypes.Add (type); - } if (possibleTypes.Count == 0) { throw new NotSupportedException ( $"Could not find System.Type corresponding to Java type `{jniType}` within constructor signature `{signature}`.", @@ -296,18 +280,12 @@ static unsafe void RegisterNativeMembers ( var methodsRef = new JniObjectReference (n_methods); var typeSig = new JniTypeSignature (nativeClass.Name); - + var type = GetTypeFromSignature (JniEnvironment.Runtime.TypeManager, typeSig); #if NET int methodsLength = JniEnvironment.Strings.GetStringLength (methodsRef); var methodsChars = JniEnvironment.Strings.GetStringChars (methodsRef, null); var methods = new ReadOnlySpan(methodsChars, methodsLength); - Type? type = null; try { - if (typeSig.SimpleReference != null && - JniRuntime.JniTypeManager.TryRegisterBuiltInNativeMembers (nativeClass, typeSig.SimpleReference, methods)) - return; - - type = GetTypeFromSignature (JniEnvironment.Runtime.TypeManager, typeSig); JniEnvironment.Runtime.TypeManager.RegisterNativeMembers (nativeClass, type, methods); } catch (Exception e) { @@ -319,7 +297,6 @@ static unsafe void RegisterNativeMembers ( JniEnvironment.Strings.ReleaseStringChars (methodsRef, methodsChars); } #else // NET - var type = GetTypeFromSignature (JniEnvironment.Runtime.TypeManager, typeSig); var methods = JniEnvironment.Strings.ToString (methodsRef); JniEnvironment.Runtime.TypeManager.RegisterNativeMembers (nativeClass, type, methods); #endif // NET From 32f4f4f9c64a2b1edac8692d95e86d0a8654c158 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 9 Jun 2026 11:50:31 +0200 Subject: [PATCH 19/28] Remove unnecessary changes --- src/Java.Interop/Java.Interop/ManagedPeer.cs | 14 ++++++++++++- .../Java.Interop/JavaVMFixture.Partial.cs | 21 +++++++------------ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/Java.Interop/Java.Interop/ManagedPeer.cs b/src/Java.Interop/Java.Interop/ManagedPeer.cs index 5fffb81a6..cfd05c530 100644 --- a/src/Java.Interop/Java.Interop/ManagedPeer.cs +++ b/src/Java.Interop/Java.Interop/ManagedPeer.cs @@ -75,12 +75,24 @@ static void Construct ( Console.WriteLine ($"error: could not begin ManagedPeer.Construct!"); return; } + try { var runtime = JniEnvironment.Runtime; var r_self = new JniObjectReference (n_self); var self = runtime.ValueManager.PeekPeer (r_self); + if (self != null) { var state = self.JniManagedPeerState; - JniEnvironment.Runtime.TypeManager.RegisterNativeMembers (nativeClass, typeSig, methods); + if ((state & JniManagedPeerStates.Activatable) != JniManagedPeerStates.Activatable && + (state & JniManagedPeerStates.Replaceable) != JniManagedPeerStates.Replaceable) { + return; + } + } + + if (JniEnvironment.WithinNewObjectScope) { + if (runtime.ObjectReferenceManager.LogGlobalReferenceMessages) { + runtime.ObjectReferenceManager.WriteGlobalReferenceLine ( + "Warning: Skipping managed constructor invocation for PeerReference={0} IdentityHashCode=0x{1} Java.Type={2}. " + + "Please use JniPeerMembers.InstanceMethods.StartCreateInstance() + JniPeerMembers.InstanceMethods.FinishCreateInstance() instead of " + "JniEnvironment.Object.NewObject().", r_self, runtime.ValueManager.GetJniIdentityHashCode (r_self).ToString ("x"), diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs index 6ceb1ff53..bd7ae815f 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs @@ -3,14 +3,18 @@ using System.Reflection; using Java.Interop; -using NUnit.Framework; namespace Java.InteropTests { + [RequiresDynamicCode ("JavaVMFixture uses ReflectionJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] + [RequiresUnreferencedCode ("JavaVMFixture uses ReflectionJniTypeManager, which is reflection-based and not trimming-compatible.")] public abstract partial class JavaVMFixture { - [RequiresDynamicCode ("JavaVMFixture uses ReflectionJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("JavaVMFixture uses ReflectionJniTypeManager, which is reflection-based and not trimming-compatible.")] + static JavaVMFixture () + { + CreateJavaVM (); + } + static partial void CreateJavaVM (); // VM supports specifying a class to JNIEnv::CallNonvirtualVoidMethod() @@ -22,16 +26,5 @@ public abstract partial class JavaVMFixture { protected JavaVMFixture () { } - - - [OneTimeSetUp] - [RequiresDynamicCode ("JavaVMFixture uses ReflectionJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("JavaVMFixture uses ReflectionJniTypeManager, which is reflection-based and not trimming-compatible.")] - public void EnsureJavaVM () - { - if (VM == null) { - CreateJavaVM (); - } - } } } From 8f644e502879237b0316f6a3919a26bbfb3b9752 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 9 Jun 2026 11:54:38 +0200 Subject: [PATCH 20/28] Suppress warnigns in tests --- .../Java.Interop/ManagedValueManager.cs | 2 +- .../Java.Interop/MonoRuntimeValueManager.cs | 2 +- .../Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs | 4 ++-- tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs | 4 ++-- .../Java.Interop/JniRuntime.JniValueManagerTests.cs | 5 ++++- tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs | 4 +++- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Java.Runtime.Environment/Java.Interop/ManagedValueManager.cs b/src/Java.Runtime.Environment/Java.Interop/ManagedValueManager.cs index 223a0d052..c828f1ac8 100644 --- a/src/Java.Runtime.Environment/Java.Interop/ManagedValueManager.cs +++ b/src/Java.Runtime.Environment/Java.Interop/ManagedValueManager.cs @@ -12,7 +12,7 @@ namespace Java.Interop { - class ManagedValueManager : JniRuntime.JniValueManager { + class ManagedValueManager : JniRuntime.ReflectionJniValueManager { Dictionary>? RegisteredInstances = new Dictionary>(); diff --git a/src/Java.Runtime.Environment/Java.Interop/MonoRuntimeValueManager.cs b/src/Java.Runtime.Environment/Java.Interop/MonoRuntimeValueManager.cs index 727354429..b8b0ba7fa 100644 --- a/src/Java.Runtime.Environment/Java.Interop/MonoRuntimeValueManager.cs +++ b/src/Java.Runtime.Environment/Java.Interop/MonoRuntimeValueManager.cs @@ -15,7 +15,7 @@ enum GCBridgeUseWeakReferenceKind { Jni, } - class MonoRuntimeValueManager : JniRuntime.JniValueManager { + class MonoRuntimeValueManager : JniRuntime.ReflectionJniValueManager { #pragma warning disable 0649 // This field is mutated by the java-interop native lib diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs index bd7ae815f..63063751f 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs @@ -6,10 +6,10 @@ namespace Java.InteropTests { - [RequiresDynamicCode ("JavaVMFixture uses ReflectionJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("JavaVMFixture uses ReflectionJniTypeManager, which is reflection-based and not trimming-compatible.")] public abstract partial class JavaVMFixture { + [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "JavaVMFixture intentionally uses reflection-backed managers for non-AOT tests.")] + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "JavaVMFixture intentionally uses reflection-backed managers for non-trimming tests.")] static JavaVMFixture () { CreateJavaVM (); diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs index b20b6897e..0715b6a51 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs @@ -29,8 +29,8 @@ static partial void CreateJavaVM () } } - [RequiresDynamicCode ("JavaVMFixtureTypeManager uses ReflectionJniTypeManager, which is reflection-based and not NativeAOT-compatible.")] - [RequiresUnreferencedCode ("JavaVMFixtureTypeManager uses ReflectionJniTypeManager, which is reflection-based and not trimming-compatible.")] + [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "JavaVMFixtureTypeManager intentionally uses reflection-backed type manager behavior for tests.")] + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "JavaVMFixtureTypeManager intentionally uses reflection-backed type manager behavior for tests.")] class JavaVMFixtureTypeManager : JniRuntime.ReflectionJniTypeManager { Dictionary TypeMappings = new() { diff --git a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniValueManagerTests.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniValueManagerTests.cs index b687067e5..b88401a36 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniValueManagerTests.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniValueManagerTests.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Collections.Generic; @@ -29,7 +30,9 @@ public void CreateValue () } } - class MyValueManager : JniRuntime.JniValueManager { + [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "MyValueManager intentionally uses reflection-backed value manager behavior for tests.")] + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "MyValueManager intentionally uses reflection-backed value manager behavior for tests.")] + class MyValueManager : JniRuntime.ReflectionJniValueManager { public override void WaitForGCBridgeProcessing () { diff --git a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs index 3b3285eb9..48b3e1663 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs @@ -167,7 +167,9 @@ public override int WeakGlobalReferenceCount { } } - class ProxyValueManager : JniValueManager { + [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "ProxyValueManager intentionally uses reflection-backed value manager behavior for tests.")] + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "ProxyValueManager intentionally uses reflection-backed value manager behavior for tests.")] + class ProxyValueManager : ReflectionJniValueManager { public override void AddPeer (IJavaPeerable peer) { From 48fbe33829ee8d554f0b1e1e7c0a0558ef5eb8e2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Jun 2026 10:29:18 +0000 Subject: [PATCH 21/28] Fix code review issues in ReflectionJniValueManager and ReflectionJniTypeManager Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> --- .../JniRuntime.ReflectionJniTypeManager.cs | 1 - .../JniRuntime.ReflectionJniValueManager.cs | 15 +++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs index 410e28178..93211d57c 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs @@ -126,7 +126,6 @@ protected override IEnumerable GetSimpleReferences (Type type) yield break; } - static readonly string[] EmptyStringArray = Array.Empty (); static readonly Type[] EmptyTypeArray = Array.Empty (); readonly struct KnownArrayTypesInfo diff --git a/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs index 0b3d8e4f1..875fef93e 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs @@ -16,8 +16,8 @@ namespace Java.Interop { partial class JniRuntime { - [RequiresDynamicCode ("TODO")] - [RequiresUnreferencedCode ("TODO")] + [RequiresDynamicCode ("This JniValueManager implementation is not compatible with Native AOT. Use a different JniValueManager implementation that supports Native AOT.")] + [RequiresUnreferencedCode ("This JniValueManager implementation is not compatible with Native AOT. Use a different JniValueManager implementation that supports Native AOT.")] public abstract partial class ReflectionJniValueManager : JniValueManager { public override void ActivatePeer ( @@ -253,7 +253,7 @@ bool TryConstructPeer ( return false; } - protected override object? CreateValueCore ( + protected override object? CreateValueCore ( ref JniObjectReference reference, JniObjectReferenceOptions options, [DynamicallyAccessedMembers (Constructors)] @@ -480,12 +480,19 @@ protected override JniValueMarshaler GetValueMarshalerCore (Type type) static JniValueMarshaler GetObjectArrayMarshaler (Type elementType) { - Func indirect = static () => JavaObjectArray.Instance; + Func indirect = GetObjectArrayMarshalerHelper; var reifiedMethodInfo = indirect.Method.GetGenericMethodDefinition ().MakeGenericMethod (elementType); Func direct = (Func) Delegate.CreateDelegate (typeof (Func), reifiedMethodInfo); return direct (); } + static JniValueMarshaler GetObjectArrayMarshalerHelper< + [DynamicallyAccessedMembers (Constructors)] + T> () + { + return JavaObjectArray.Instance; + } + } sealed class VoidValueMarshaler : JniValueMarshaler { From 5dbaa73a903791fac41d24c44d06ef7b6fc1267d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Jun 2026 11:03:17 +0000 Subject: [PATCH 22/28] Address review comments: EnsureNotDisposed, spacing, Array.Empty, null-forgiving operator Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> --- .../Java.Interop/JniRuntime.ReflectionJniTypeManager.cs | 4 ++-- .../Java.Interop/JniRuntime.ReflectionJniValueManager.cs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs index 93211d57c..5f1394ebb 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs @@ -88,7 +88,7 @@ static Type GetUnderlyingType (Type type, out int rank) if (type.IsArray && type.GetArrayRank () > 1) throw new ArgumentException ("Multidimensional array '" + originalType.FullName + "' is not supported.", nameof (type)); rank++; - type = type.GetElementType ()!; + type = type.GetElementType () ?? throw new InvalidOperationException ("Array type has no element type."); } if (type.IsEnum) @@ -126,7 +126,7 @@ protected override IEnumerable GetSimpleReferences (Type type) yield break; } - static readonly Type[] EmptyTypeArray = Array.Empty (); + static readonly Type[] EmptyTypeArray = []; readonly struct KnownArrayTypesInfo { diff --git a/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs index 875fef93e..70ba47414 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs @@ -375,6 +375,7 @@ bool TryConstructPeer ( [DynamicallyAccessedMembers (Constructors)] Type? targetType = null) { + EnsureNotDisposed (); if (!reference.IsValid) { #pragma warning disable 8653 return default (T); @@ -469,7 +470,7 @@ protected override JniValueMarshaler GetValueMarshalerCore (Type type) return ProxyValueMarshaler.Instance; } - static Type? GetListType(Type type) + static Type? GetListType (Type type) { foreach (var iface in type.GetInterfaces ().Concat (new [] { type })) { if (typeof (IList<>).IsAssignableFrom (iface.IsGenericType ? iface.GetGenericTypeDefinition () : iface)) From 6021897d5221c737c23765a42dcaa9ad2a3a5c57 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 9 Jun 2026 17:30:20 +0200 Subject: [PATCH 23/28] Suppress NativeAOT warnings in JRE value managers Java.Runtime.Environment remains the standalone desktop/JRE runtime path until #1447 removes it. Suppress the reflection-based value manager trim/AOT diagnostics on its constructors so existing NativeAOT samples can continue to publish while that cleanup is in flight. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Java.Interop/ManagedValueManager.cs | 6 ++++++ .../Java.Interop/MonoRuntimeValueManager.cs | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Java.Runtime.Environment/Java.Interop/ManagedValueManager.cs b/src/Java.Runtime.Environment/Java.Interop/ManagedValueManager.cs index c828f1ac8..a17c3ae2e 100644 --- a/src/Java.Runtime.Environment/Java.Interop/ManagedValueManager.cs +++ b/src/Java.Runtime.Environment/Java.Interop/ManagedValueManager.cs @@ -16,6 +16,12 @@ class ManagedValueManager : JniRuntime.ReflectionJniValueManager { Dictionary>? RegisteredInstances = new Dictionary>(); + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Java.Runtime.Environment is the desktop/JRE runtime path and is removed by dotnet/java-interop#1447.")] + [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Java.Runtime.Environment is the desktop/JRE runtime path and is removed by dotnet/java-interop#1447.")] + public ManagedValueManager () + { + } + public override void WaitForGCBridgeProcessing () { } diff --git a/src/Java.Runtime.Environment/Java.Interop/MonoRuntimeValueManager.cs b/src/Java.Runtime.Environment/Java.Interop/MonoRuntimeValueManager.cs index b8b0ba7fa..2d9e409a4 100644 --- a/src/Java.Runtime.Environment/Java.Interop/MonoRuntimeValueManager.cs +++ b/src/Java.Runtime.Environment/Java.Interop/MonoRuntimeValueManager.cs @@ -24,6 +24,12 @@ class MonoRuntimeValueManager : JniRuntime.ReflectionJniValueManager { IntPtr bridge; + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Java.Runtime.Environment is the desktop/JRE runtime path and is removed by dotnet/java-interop#1447.")] + [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Java.Runtime.Environment is the desktop/JRE runtime path and is removed by dotnet/java-interop#1447.")] + public MonoRuntimeValueManager () + { + } + public override void OnSetRuntime (JniRuntime runtime) { base.OnSetRuntime (runtime); @@ -415,4 +421,3 @@ public override string StackTrace { } } } - From f3b263ef64f6e0d2a524cef3dcd3f0af92e70042 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Jun 2026 16:12:19 +0000 Subject: [PATCH 24/28] Fix NativeAOT test failure: handle built-in types in JniTypeManager.GetBuiltInType Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> --- .../NativeAotTypeManager.cs | 2 ++ .../NativeAotTypeManager.cs | 3 +++ .../Java.Interop/JniRuntime.JniTypeManager.cs | 19 +++++++++++++++++-- src/Java.Interop/PublicAPI.Unshipped.txt | 1 + 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs index acb33d7a4..5a0d709b7 100644 --- a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs @@ -26,6 +26,8 @@ public override void RegisterNativeMembers ( Type type, ReadOnlySpan methods) { + if (TryRegisterBuiltInNativeMembers (nativeClass, nativeClass.Name, methods)) + return; if (!methods.IsEmpty) throw new NotSupportedException ($"Could not register native members for type '{type.FullName}'."); } diff --git a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs index baa89a579..8228b6229 100644 --- a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs @@ -104,6 +104,9 @@ public override void RegisterNativeMembers ( 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}'."); diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs index 8c015d1ed..f9cf92300 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs @@ -221,11 +221,26 @@ static JniTypeSignature GetBuiltInTypeSignature (Type type) return default; } + // IL2026/IL2111: The MethodsConstructors DAM annotation on the return type causes ILLink to analyze + // JavaProxyObject/JavaProxyThrowable/ManagedPeer's delegate-typed nested members, whose base + // constructors (Delegate.Delegate(Object,String)) are marked RequiresUnreferencedCode. + // These warnings are false positives: the delegate constructors are invoked with + // compile-time-known static method references, not via string-based reflection. + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Delegate constructors in JavaProxyObject/JavaProxyThrowable/ManagedPeer are invoked with compile-time-known method references, not via reflection.")] + [UnconditionalSuppressMessage ("Trimming", "IL2111", Justification = "Delegate constructors in JavaProxyObject/JavaProxyThrowable/ManagedPeer are invoked with compile-time-known method references, not via reflection.")] [return: DynamicallyAccessedMembers (MethodsConstructors)] static Type? GetBuiltInType (JniTypeSignature typeSignature) { - if (!typeSignature.IsKeyword || typeSignature.ArrayRank != 0) + if (typeSignature.ArrayRank != 0) return null; + if (!typeSignature.IsKeyword) { + return typeSignature.SimpleReference switch { + JavaProxyObject.JniTypeName => typeof (JavaProxyObject), + JavaProxyThrowable.JniTypeName => typeof (JavaProxyThrowable), + ManagedPeer.JniTypeName => typeof (ManagedPeer), + _ => null, + }; + } return typeSignature.SimpleReference switch { "V" => TypeOfVoid (), "Z" => TypeOf (), @@ -249,7 +264,7 @@ static Type TypeOf< static Type TypeOfVoid () => typeof (void); #if NET - internal static bool TryRegisterBuiltInNativeMembers ( + protected static bool TryRegisterBuiltInNativeMembers ( JniType nativeClass, string jniSimpleReference, ReadOnlySpan methods) diff --git a/src/Java.Interop/PublicAPI.Unshipped.txt b/src/Java.Interop/PublicAPI.Unshipped.txt index 7dc728c0c..ebb4bec65 100644 --- a/src/Java.Interop/PublicAPI.Unshipped.txt +++ b/src/Java.Interop/PublicAPI.Unshipped.txt @@ -8,6 +8,7 @@ abstract Java.Interop.JniRuntime.JniTypeManager.GetReflectionConstructibleTypes( 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! +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 From 65b472d9f340b35e558a4af20d5d8d0bfda3bf19 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Jun 2026 16:26:28 +0000 Subject: [PATCH 25/28] Resolve merge conflicts: remove #if NET guards, keep abstract methods in base JniTypeManager Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> --- .../Java.Interop/JniRuntime.JniTypeManager.cs | 191 +----------------- .../JniRuntime.ReflectionJniTypeManager.cs | 11 - src/Java.Interop/Java.Interop/ManagedPeer.cs | 4 - .../Java.Interop/JreRuntime.cs | 6 - 4 files changed, 2 insertions(+), 210 deletions(-) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs index 377098703..9d7b1964a 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs @@ -261,7 +261,6 @@ static Type TypeOf< [return: DynamicallyAccessedMembers (MethodsConstructors)] static Type TypeOfVoid () => typeof (void); -#if NET protected static bool TryRegisterBuiltInNativeMembers ( JniType nativeClass, string jniSimpleReference, @@ -277,7 +276,6 @@ protected static bool TryRegisterBuiltInNativeMembers ( return jniSimpleReference == JavaProxyThrowable.JniTypeName && methods.IsEmpty; } -#endif /// [return: DynamicallyAccessedMembers (Constructors)] @@ -292,51 +290,9 @@ protected static bool TryRegisterBuiltInNativeMembers ( } [return: DynamicallyAccessedMembers (Constructors)] -<<<<<<< HEAD protected abstract Type? GetInvokerTypeCore ([DynamicallyAccessedMembers (Constructors)] Type type); -#if NET - protected abstract IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimple); -======= - protected virtual 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; - } - - Type[] arguments = type.GetGenericArguments (); - if (arguments.Length == 0) - return signature.InvokerType; - - return MakeGenericType (signature.InvokerType, arguments); - } - - public IReadOnlyList? GetStaticMethodFallbackTypes (string jniSimpleReference) - { - AssertValid (); - AssertSimpleReference (jniSimpleReference, nameof (jniSimpleReference)); - - return GetStaticMethodFallbackTypesCore (jniSimpleReference); - } - - protected virtual IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimple) => null; ->>>>>>> origin/main + protected abstract IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimple); public string? GetReplacementType (string jniSimpleReference) { @@ -376,157 +332,14 @@ public abstract void RegisterNativeMembers ( JniType nativeClass, [DynamicallyAccessedMembers (MethodsAndPrivateNested)] Type type, -<<<<<<< HEAD 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); -======= - ReadOnlySpan methods) - { - TryRegisterNativeMembers (nativeClass, type, methods); - } - - protected bool TryRegisterNativeMembers ( - JniType nativeClass, - [DynamicallyAccessedMembers (MethodsAndPrivateNested)] - Type type, - ReadOnlySpan methods) - { - AssertValid (); - -#pragma warning disable CS1717 - methods = methods; -#pragma warning restore CS1717 - - return TryLoadJniMarshalMethods (nativeClass, type, null) || TryRegisterNativeMembers (nativeClass, type, null, null); - } - - [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] - public virtual void RegisterNativeMembers ( - JniType nativeClass, - [DynamicallyAccessedMembers (MethodsAndPrivateNested)] - Type type, - string? methods) - { - TryRegisterNativeMembers (nativeClass, type, methods); - } - - [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] - protected bool TryRegisterNativeMembers ( - JniType nativeClass, - [DynamicallyAccessedMembers (MethodsAndPrivateNested)] - Type type, - string? methods) - { - AssertValid (); - - return TryLoadJniMarshalMethods (nativeClass, type, methods) || TryRegisterNativeMembers (nativeClass, type, methods, null); - } - - static Type [] registerMethodParameters = new Type [] { typeof (JniNativeMethodRegistrationArguments) }; - - // https://github.com/xamarin/xamarin-android/blob/5472eec991cc075e4b0c09cd98a2331fb93aa0f3/src/Microsoft.Android.Sdk.ILLink/PreserveRegistrations.cs#L85 - const string MarshalMethods = "'jni_marshal_methods' is preserved by the PreserveRegistrations trimmer step."; - - [UnconditionalSuppressMessage ("Trimming", "IL2072", Justification = MarshalMethods)] - [UnconditionalSuppressMessage ("Trimming", "IL2075", Justification = MarshalMethods)] - bool TryLoadJniMarshalMethods ( - JniType nativeClass, - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] - Type type, - string? methods) - { - var marshalType = type?.GetNestedType ("__<$>_jni_marshal_methods", BindingFlags.NonPublic); - if (marshalType == null) { - return false; - } - - var registerMethod = marshalType.GetMethod ( - name: "__RegisterNativeMembers", - bindingAttr: BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, - binder: null, - callConvention: default, - types: registerMethodParameters, - modifiers: null); - return TryRegisterNativeMembers (nativeClass, marshalType, methods, registerMethod); - } - - static List sharedRegistrations = new List (); - - bool TryRegisterNativeMembers ( - JniType nativeClass, - [DynamicallyAccessedMembers (Methods)] - Type marshalType, - string? methods, - MethodInfo? registerMethod) - { - bool lockTaken = false; - bool rv = false; - - try { - Monitor.TryEnter (sharedRegistrations, ref lockTaken); - List registrations; - if (lockTaken) { - sharedRegistrations.Clear (); - registrations = sharedRegistrations; - } else { - registrations = new List (); - } - JniNativeMethodRegistrationArguments arguments = new JniNativeMethodRegistrationArguments (registrations, methods); - if (registerMethod != null) { - registerMethod.Invoke (null, new object [] { arguments }); - rv = true; - } else - rv = FindAndCallRegisterMethod (marshalType, arguments); - - if (registrations.Count > 0) - nativeClass.RegisterNativeMethods (registrations.ToArray ()); - } finally { - if (lockTaken) { - Monitor.Exit (sharedRegistrations); - } - } - - return rv; - } - - bool FindAndCallRegisterMethod ( - [DynamicallyAccessedMembers (Methods)] - Type marshalType, - JniNativeMethodRegistrationArguments arguments) - { - if (!Runtime.JniAddNativeMethodRegistrationAttributePresent) - return false; - - bool found = false; - - foreach (var methodInfo in marshalType.GetRuntimeMethods ()) { - if (methodInfo.GetCustomAttribute (typeof (JniAddNativeMethodRegistrationAttribute)) == null) { - continue; - } - - var declaringTypeName = methodInfo.DeclaringType?.FullName ?? ""; - - if ((methodInfo.Attributes & MethodAttributes.Static) != MethodAttributes.Static) { - throw new InvalidOperationException ($"The method `{declaringTypeName}.{methodInfo}` marked with [{nameof (JniAddNativeMethodRegistrationAttribute)}] must be static!"); - } - - var register = (Action)methodInfo.CreateDelegate (typeof (Action)); - register (arguments); - - found = true; - } - - return found; - } ->>>>>>> origin/main } } } diff --git a/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs index 5f1394ebb..c47488f91 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs @@ -355,15 +355,9 @@ IEnumerable CreateGetTypesForSimpleReferenceEnumerator (string jniSimpleRe if (arguments.Length == 0) return signature.InvokerType; -#if NET throw new NotSupportedException ($"Generic invoker type construction for `{type}` is not supported."); -#else // NET - return signature.InvokerType.MakeGenericType (arguments); -#endif // NET } -#if NET - protected override IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimple) => null; protected override string? GetReplacementTypeCore (string jniSimpleReference) => null; @@ -393,11 +387,8 @@ protected bool TryRegisterNativeMembers ( return TryLoadJniMarshalMethods (nativeClass, type, null) || TryRegisterNativeMembers (nativeClass, type, null, null); } -#endif // NET -#if NET [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] -#endif // NET public override void RegisterNativeMembers ( JniType nativeClass, [DynamicallyAccessedMembers (MethodsAndPrivateNested)] @@ -407,9 +398,7 @@ public override void RegisterNativeMembers ( TryRegisterNativeMembers (nativeClass, type, methods); } -#if NET [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] -#endif // NET protected bool TryRegisterNativeMembers ( JniType nativeClass, [DynamicallyAccessedMembers (MethodsAndPrivateNested)] diff --git a/src/Java.Interop/Java.Interop/ManagedPeer.cs b/src/Java.Interop/Java.Interop/ManagedPeer.cs index c56b5a753..0a03e916c 100644 --- a/src/Java.Interop/Java.Interop/ManagedPeer.cs +++ b/src/Java.Interop/Java.Interop/ManagedPeer.cs @@ -293,11 +293,7 @@ static unsafe void RegisterNativeMembers ( var typeSig = new JniTypeSignature (nativeClass.Name); var type = GetTypeFromSignature (JniEnvironment.Runtime.TypeManager, typeSig); -<<<<<<< HEAD -#if NET -======= ->>>>>>> origin/main int methodsLength = JniEnvironment.Strings.GetStringLength (methodsRef); var methodsChars = JniEnvironment.Strings.GetStringChars (methodsRef, null); var methods = new ReadOnlySpan(methodsChars, methodsLength); diff --git a/src/Java.Runtime.Environment/Java.Interop/JreRuntime.cs b/src/Java.Runtime.Environment/Java.Interop/JreRuntime.cs index 771dcb32c..c92d69b5a 100644 --- a/src/Java.Runtime.Environment/Java.Interop/JreRuntime.cs +++ b/src/Java.Runtime.Environment/Java.Interop/JreRuntime.cs @@ -91,9 +91,7 @@ static unsafe JreRuntimeOptions CreateJreVM (JreRuntimeOptions builder) { if (builder == null) throw new ArgumentNullException ("builder"); -#if NET builder.TypeManager ??= new JreTypeManager (builder.typeMappings); -#endif // NET return CreateJreVMCore (builder); } @@ -115,11 +113,7 @@ static unsafe JreRuntimeOptions CreateJreVMCore (JreRuntimeOptions builder) builder.LibraryHandler = JvmLibraryHandler.Create (); -<<<<<<< HEAD -======= - builder.TypeManager ??= new JreTypeManager (builder.typeMappings); ->>>>>>> origin/main bool onMono = Type.GetType ("Mono.RuntimeStructs", throwOnError: false) != null; if (onMono) { Console.WriteLine ($"MonoVM support enabled"); From 94c0cd9736a747f8c416abec21c015551a41b602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Rozs=C3=ADval?= Date: Tue, 9 Jun 2026 22:20:58 +0200 Subject: [PATCH 26/28] Remove unnecessary code --- .../Java.Interop/JniRuntime.JniTypeManager.cs | 26 ++++------- .../JniRuntime.ReflectionJniTypeManager.cs | 44 ++++++++----------- 2 files changed, 27 insertions(+), 43 deletions(-) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs index 9d7b1964a..7cab46c5c 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs @@ -240,27 +240,19 @@ static JniTypeSignature GetBuiltInTypeSignature (Type type) }; } return typeSignature.SimpleReference switch { - "V" => TypeOfVoid (), - "Z" => TypeOf (), - "B" => TypeOf (), - "C" => TypeOf (), - "S" => TypeOf (), - "I" => TypeOf (), - "J" => TypeOf (), - "F" => TypeOf (), - "D" => TypeOf (), + "V" => typeof (void), + "Z" => typeof (bool), + "B" => typeof (sbyte), + "C" => typeof (char), + "S" => typeof (short), + "I" => typeof (int), + "J" => typeof (long), + "F" => typeof (float), + "D" => typeof (double), _ => null, }; } - [return: DynamicallyAccessedMembers (MethodsConstructors)] - static Type TypeOf< - [DynamicallyAccessedMembers (MethodsConstructors)] - T> () => typeof (T); - - [return: DynamicallyAccessedMembers (MethodsConstructors)] - static Type TypeOfVoid () => typeof (void); - protected static bool TryRegisterBuiltInNativeMembers ( JniType nativeClass, string jniSimpleReference, diff --git a/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs index c47488f91..b98ec2843 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs @@ -207,38 +207,30 @@ static Type MakeJavaObjectArrayType (Type type) => AssertSimpleReference (jniSimpleReference); return jniSimpleReference switch { - "java/lang/String" => TypeOf (), + "java/lang/String" => typeof (string), "net/dot/jni/internal/JavaProxyObject" => typeof (JavaProxyObject), "net/dot/jni/internal/JavaProxyThrowable" => typeof (JavaProxyThrowable), - "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 (), + "V" => typeof (void), + "Z" => typeof (Boolean), + "java/lang/Boolean" => typeof (Boolean?), + "B" => typeof (SByte), + "java/lang/Byte" => typeof (SByte?), + "C" => typeof (Char), + "java/lang/Character" => typeof (Char?), + "S" => typeof (Int16), + "java/lang/Short" => typeof (Int16?), + "I" => typeof (Int32), + "java/lang/Integer" => typeof (Int32?), + "J" => typeof (Int64), + "java/lang/Long" => typeof (Int64?), + "F" => typeof (Single), + "java/lang/Float" => typeof (Single?), + "D" => typeof (Double), + "java/lang/Double" => typeof (Double?), _ => null, }; } - [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 (); From 8247f98ab6345b5dfcf8babbe40ff6d5c331ddf4 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 9 Jun 2026 23:16:08 +0200 Subject: [PATCH 27/28] Fix NativeAOT sample primitive type lookup Map JNI primitive and boxed primitive signatures in the NativeAOT sample type managers so constructor signature matching can resolve primitive parameters such as I to System.Int32. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../NativeAotTypeManager.cs | 17 +++++++++++++++++ .../NativeAotTypeManager.cs | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs index 5a0d709b7..caa5187a7 100644 --- a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs @@ -58,6 +58,23 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl 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), diff --git a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs index 8228b6229..e1a3150b3 100644 --- a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs @@ -19,6 +19,23 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl 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), From e2fb74e5d4beb94beaad3e4a9fc343dfe1ce3f66 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Tue, 9 Jun 2026 17:07:14 -0500 Subject: [PATCH 28/28] Keep JniTypeManager non-abstract; track API breaks via *REMOVED* To minimize migration pain for callers moving from .NET 10 to .NET 11, revert JniTypeManager from `abstract` back to a regular class. Methods that were turned into `abstract` overrides now have `virtual` defaults so existing subclasses keep compiling: * Type lookup methods return null/empty defaults. * RegisterNativeMembers is a no-op by default; reflection-based behavior remains in ReflectionJniTypeManager (override to opt-in). Restore PublicAPI.Shipped.txt to its main-branch state (per the `Microsoft.CodeAnalysis.PublicApiAnalyzers` convention, never edit shipped entries in place) and track every breaking change in PublicAPI.Unshipped.txt: * 5 new JniTypeManager.* APIs are now `virtual` (not `abstract`). * `*REMOVED*` entries for the two `JniTypeManager.TryRegisterNativeMembers` overloads that moved to `ReflectionJniTypeManager`. * `*REMOVED*virtual` + `abstract` pairs for the two virtual->abstract transitions on `JniValueManager` (CreatePeer, GetValueMarshalerCore(Type)). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Java.Interop/JniRuntime.JniTypeManager.cs | 40 +++++++++++-------- src/Java.Interop/PublicAPI.Shipped.txt | 24 ++++++----- src/Java.Interop/PublicAPI.Unshipped.txt | 16 +++++--- 3 files changed, 47 insertions(+), 33 deletions(-) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs index 7cab46c5c..379b70847 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs @@ -78,7 +78,7 @@ public override string ToString () } /// - public abstract partial class JniTypeManager : IDisposable, ISetRuntime { + public partial class JniTypeManager : IDisposable, ISetRuntime { internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; internal const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods; @@ -148,7 +148,7 @@ public JniTypeSignature GetTypeSignature (Type type) return builtIn.IsValid ? builtIn : GetTypeSignatureCore (type); } - protected abstract JniTypeSignature GetTypeSignatureCore (Type type); + protected virtual JniTypeSignature GetTypeSignatureCore (Type type) => default; // NOTE: This method needs to be kept in sync with GetTypeSignature() public IEnumerable GetTypeSignatures (Type type) @@ -165,7 +165,7 @@ public IEnumerable GetTypeSignatures (Type type) return GetTypeSignaturesCore (type); } - protected abstract IEnumerable GetTypeSignaturesCore (Type type); + protected virtual IEnumerable GetTypeSignaturesCore (Type type) => []; [return: DynamicallyAccessedMembers (MethodsConstructors)] public Type? GetType (JniTypeSignature typeSignature) @@ -187,13 +187,13 @@ public IEnumerable GetTypeSignatures (Type 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); + protected virtual string? GetSimpleReference (Type type) => null; + protected virtual IEnumerable GetSimpleReferences (Type type) => []; [return: DynamicallyAccessedMembers (MethodsConstructors)] - protected abstract Type? GetTypeForSimpleReference (string jniSimpleReference); - public abstract IEnumerable GetTypes (JniTypeSignature typeSignature); + protected virtual Type? GetTypeForSimpleReference (string jniSimpleReference) => null; + public virtual IEnumerable GetTypes (JniTypeSignature typeSignature) => []; - public abstract IEnumerable GetReflectionConstructibleTypes (JniTypeSignature typeSignature); + public virtual IEnumerable GetReflectionConstructibleTypes (JniTypeSignature typeSignature) => []; public class ReflectionConstructibleType { @@ -208,7 +208,7 @@ public ReflectionConstructibleType ( public Type Type { get; } } - protected abstract IEnumerable GetTypesForSimpleReference (string jniSimpleReference); + protected virtual IEnumerable GetTypesForSimpleReference (string jniSimpleReference) => []; static JniTypeSignature GetBuiltInTypeSignature (Type type) { @@ -282,9 +282,9 @@ protected static bool TryRegisterBuiltInNativeMembers ( } [return: DynamicallyAccessedMembers (Constructors)] - protected abstract Type? GetInvokerTypeCore ([DynamicallyAccessedMembers (Constructors)] Type type); + protected virtual Type? GetInvokerTypeCore ([DynamicallyAccessedMembers (Constructors)] Type type) => null; - protected abstract IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimple); + protected virtual IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimple) => null; public string? GetReplacementType (string jniSimpleReference) { @@ -294,7 +294,7 @@ protected static bool TryRegisterBuiltInNativeMembers ( return GetReplacementTypeCore (jniSimpleReference); } - protected abstract string? GetReplacementTypeCore (string jniSimpleReference); + protected virtual string? GetReplacementTypeCore (string jniSimpleReference) => null; public IReadOnlyList? GetStaticMethodFallbackTypes (string jniSimpleReference) { @@ -318,20 +318,26 @@ protected static bool TryRegisterBuiltInNativeMembers ( return GetReplacementMethodInfoCore (jniSimpleReference, jniMethodName, jniMethodSignature); } - protected abstract ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSimpleReference, string jniMethodName, string jniMethodSignature); + protected virtual ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSimpleReference, string jniMethodName, string jniMethodSignature) => null; - public abstract void RegisterNativeMembers ( + // Default implementation is a no-op. Derived classes (e.g. `ReflectionJniTypeManager`) + // provide reflection-based registration. Override to provide custom registration. + public virtual void RegisterNativeMembers ( JniType nativeClass, [DynamicallyAccessedMembers (MethodsAndPrivateNested)] Type type, - ReadOnlySpan methods); + ReadOnlySpan methods) + { + } [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] - public abstract void RegisterNativeMembers ( + public virtual void RegisterNativeMembers ( JniType nativeClass, [DynamicallyAccessedMembers (MethodsAndPrivateNested)] Type type, - string? methods); + string? methods) + { + } } } } diff --git a/src/Java.Interop/PublicAPI.Shipped.txt b/src/Java.Interop/PublicAPI.Shipped.txt index b03076199..e1a948397 100644 --- a/src/Java.Interop/PublicAPI.Shipped.txt +++ b/src/Java.Interop/PublicAPI.Shipped.txt @@ -418,6 +418,8 @@ 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? @@ -897,21 +899,21 @@ 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 -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.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! virtual Java.Interop.JniRuntime.JniTypeManager.OnSetRuntime(Java.Interop.JniRuntime! runtime) -> 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 -abstract Java.Interop.JniRuntime.JniValueManager.CreatePeer(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions transfer, System.Type? targetType) -> Java.Interop.IJavaPeerable? +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 +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 virtual Java.Interop.JniRuntime.JniValueManager.DisposePeerUnlessReferenced(Java.Interop.IJavaPeerable! value) -> void -abstract Java.Interop.JniRuntime.JniValueManager.GetValueMarshalerCore(System.Type! type) -> Java.Interop.JniValueMarshaler! +virtual Java.Interop.JniRuntime.JniValueManager.GetValueMarshalerCore(System.Type! type) -> Java.Interop.JniValueMarshaler! virtual Java.Interop.JniRuntime.JniValueManager.OnSetRuntime(Java.Interop.JniRuntime! runtime) -> void virtual Java.Interop.JniRuntime.JniValueManager.TryUnboxPeerObject(Java.Interop.IJavaPeerable! value, out object? result) -> bool virtual Java.Interop.JniRuntime.RaisePendingException(System.Exception! pendingException) -> void diff --git a/src/Java.Interop/PublicAPI.Unshipped.txt b/src/Java.Interop/PublicAPI.Unshipped.txt index ebb4bec65..de95bcd1d 100644 --- a/src/Java.Interop/PublicAPI.Unshipped.txt +++ b/src/Java.Interop/PublicAPI.Unshipped.txt @@ -3,11 +3,11 @@ 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 -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.JniTypeManager.GetInvokerTypeCore(System.Type! type) -> System.Type? +virtual Java.Interop.JniRuntime.JniTypeManager.GetReflectionConstructibleTypes(Java.Interop.JniTypeSignature typeSignature) -> System.Collections.Generic.IEnumerable! +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 @@ -56,6 +56,12 @@ abstract Java.Interop.JniRuntime.JniValueManager.CreateValueCore(ref Java.Int abstract Java.Interop.JniRuntime.JniValueManager.GetValueCore(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type? targetType = null) -> object? abstract Java.Interop.JniRuntime.JniValueManager.GetValueCore(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type? targetType = null) -> T abstract Java.Interop.JniRuntime.JniValueManager.GetValueMarshalerCore() -> Java.Interop.JniValueMarshaler! +*REMOVED*Java.Interop.JniRuntime.JniTypeManager.TryRegisterNativeMembers(Java.Interop.JniType! nativeClass, System.Type! type, string? methods) -> bool +*REMOVED*Java.Interop.JniRuntime.JniTypeManager.TryRegisterNativeMembers(Java.Interop.JniType! nativeClass, System.Type! type, System.ReadOnlySpan methods) -> bool +*REMOVED*virtual Java.Interop.JniRuntime.JniValueManager.CreatePeer(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions transfer, System.Type? targetType) -> Java.Interop.IJavaPeerable? +abstract Java.Interop.JniRuntime.JniValueManager.CreatePeer(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions transfer, System.Type? targetType) -> Java.Interop.IJavaPeerable? +*REMOVED*virtual Java.Interop.JniRuntime.JniValueManager.GetValueMarshalerCore(System.Type! type) -> Java.Interop.JniValueMarshaler! +abstract Java.Interop.JniRuntime.JniValueManager.GetValueMarshalerCore(System.Type! type) -> Java.Interop.JniValueMarshaler! override Java.Interop.JniRuntime.ReflectionJniValueManager.ActivatePeer(Java.Interop.JniObjectReference reference, System.Type! type, System.Reflection.ConstructorInfo! cinfo, object?[]? argumentValues) -> void override Java.Interop.JniRuntime.ReflectionJniValueManager.ConstructPeerCore(Java.Interop.IJavaPeerable! peer, ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options) -> void override Java.Interop.JniRuntime.ReflectionJniValueManager.CreatePeer(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions transfer, System.Type? targetType) -> Java.Interop.IJavaPeerable?