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-NativeAOTFromAndroid/NativeAotTypeManager.cs b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs index 22f5bd7f9..acb33d7a4 100644 --- a/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs @@ -8,6 +8,7 @@ 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), @@ -25,28 +26,50 @@ public override void RegisterNativeMembers ( Type type, 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}'."); } + [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] + public override void RegisterNativeMembers ( + JniType nativeClass, + [DynamicallyAccessedMembers (MethodsAndPrivateNested)] + Type type, + string? methods) + { + RegisterNativeMembers (nativeClass, type, methods.AsSpan ()); + } protected override IEnumerable GetTypesForSimpleReference (string jniSimpleReference) { - Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}`"); - if (typeMappings.TryGetValue (jniSimpleReference, out var target)) { - Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}` -> `{target}`"); + var target = GetTypeForSimpleReference (jniSimpleReference); + if (target != null) yield return target; - } - foreach (var t in base.GetTypesForSimpleReference (jniSimpleReference)) { - Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}` -> `{t}`"); - yield return t; - } + } + + protected override string? GetSimpleReference (Type type) + { + return GetSimpleReferences (type).FirstOrDefault (); + } + + [return: DynamicallyAccessedMembers (MethodsConstructors)] + protected override Type? GetTypeForSimpleReference (string jniSimpleReference) + { + return jniSimpleReference switch { + "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, + }; } protected override IEnumerable GetSimpleReferences (Type type) { - return base.GetSimpleReferences (type) - .Concat (CreateSimpleReferencesEnumerator (type)); + return CreateSimpleReferencesEnumerator (type); } IEnumerable CreateSimpleReferencesEnumerator (Type type) @@ -58,4 +81,56 @@ 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) + { + if (!typeSignature.IsValid || typeSignature.ArrayRank != 0 || typeSignature.SimpleReference == null) + yield break; + var target = GetTypeForSimpleReference (typeSignature.SimpleReference); + if (target != null) + yield return new JniRuntime.JniTypeManager.ReflectionConstructibleType (target); + } + + protected override JniTypeSignature GetTypeSignatureCore (Type type) + { + var simpleReference = GetSimpleReferences (type).FirstOrDefault (); + return simpleReference == null ? default : new JniTypeSignature (simpleReference, 0, false); + } + + protected override IEnumerable GetTypeSignaturesCore (Type type) + { + var signature = GetTypeSignatureCore (type); + if (signature.IsValid) + yield return signature; + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + protected override Type? GetInvokerTypeCore ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type) + { + return null; + } + + protected override IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimpleReference) + { + return null; + } + + protected override string? GetReplacementTypeCore (string jniSimpleReference) + { + return null; + } + + protected override JniRuntime.ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSourceType, string jniMethodName, string jniMethodSignature) + { + return null; + } } diff --git a/samples/Hello-NativeAOTFromJNI/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/samples/Hello-NativeAOTFromJNI/ManagedType.cs b/samples/Hello-NativeAOTFromJNI/ManagedType.cs index 2c2d1f42c..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); @@ -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..baa89a579 100644 --- a/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs +++ b/samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs @@ -1,37 +1,128 @@ using Java.Interop; +using System.Diagnostics.CodeAnalysis; namespace Hello_NativeAOTFromJNI; class NativeAotTypeManager : JniRuntime.JniTypeManager { - -#pragma warning disable IL2026 - Dictionary typeMappings = new () { - [Example.ManagedType.JniTypeName] = typeof (Example.ManagedType), - }; -#pragma warning restore IL2026 - + internal const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods; + internal const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; + 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 t in base.GetTypesForSimpleReference (jniSimpleReference)) - yield return t; + } + + [return: DynamicallyAccessedMembers (MethodsConstructors)] + protected override Type? GetTypeForSimpleReference (string jniSimpleReference) + { + return jniSimpleReference switch { + Example.ManagedType.JniTypeName => typeof (Example.ManagedType), + "java/lang/Object" => typeof (Java.Lang.Object), + "java/lang/String" => typeof (Java.Lang.String), + _ => null, + }; + } + + public override IEnumerable GetTypes (JniTypeSignature typeSignature) + { + if (!typeSignature.IsValid || typeSignature.ArrayRank != 0 || typeSignature.SimpleReference == null) + return []; + return GetTypesForSimpleReference (typeSignature.SimpleReference); + } + + public override IEnumerable GetReflectionConstructibleTypes (JniTypeSignature typeSignature) + { + if (!typeSignature.IsValid || typeSignature.ArrayRank != 0 || typeSignature.SimpleReference == null) + yield break; + var target = GetTypeForSimpleReference (typeSignature.SimpleReference); + if (target != null) + yield return new JniRuntime.JniTypeManager.ReflectionConstructibleType (target); } protected override IEnumerable GetSimpleReferences (Type type) { - return 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 ( + 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 ()); + } + + [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] + 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..5c9318516 100644 --- a/src/Java.Interop/GlobalSuppressions.cs +++ b/src/Java.Interop/GlobalSuppressions.cs @@ -25,6 +25,8 @@ [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.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/JavaPrimitiveArrays.cs b/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs index dad9743ed..a7f06c76f 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 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 086f6bf07..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 JniTypeManager { + partial class ReflectionJniTypeManager { readonly struct JniPrimitiveArrayInfo { public readonly JniTypeSignature JniTypeSignature; 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/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.JniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs index e0bab4691..8c015d1ed 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,237 +146,125 @@ public JniTypeSignature GetTypeSignature (Type type) if (type == null) throw new ArgumentNullException (nameof (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; + var builtIn = GetBuiltInTypeSignature (type); + return builtIn.IsValid ? builtIn : 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) - yield break; - - 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); - } - } + return []; - 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 ()!; - } + var builtIn = GetBuiltInTypeSignature (type); + if (builtIn.IsValid) + return new [] { builtIn }; - if (type.IsEnum) - type = Enum.GetUnderlyingType (type); - - return type; + return GetTypeSignaturesCore (type); } - // `type` will NOT be an array type. - protected virtual string? GetSimpleReference (Type type) - { - return GetSimpleReferences (type).FirstOrDefault (); - } + protected abstract IEnumerable GetTypeSignaturesCore (Type type); - // `type` will NOT be an array type. - protected virtual IEnumerable GetSimpleReferences (Type type) + [return: DynamicallyAccessedMembers (MethodsConstructors)] + public Type? GetType (JniTypeSignature typeSignature) { 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 (); - const string NotUsedInAndroid = "This code path is not used in Android projects."; + if (!typeSignature.IsValid || typeSignature.SimpleReference == null) + return null; - // FIXME: https://github.com/dotnet/java-interop/issues/1192 - [UnconditionalSuppressMessage ("Trimming", "IL3050", Justification = NotUsedInAndroid)] - static Type MakeArrayType (Type type) => - type.MakeArrayType (); + var builtIn = GetBuiltInType (typeSignature); + if (builtIn != null) + return builtIn; - // 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); + 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."); + } - [UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "Types returned here should be preserved via other means.")] + protected abstract string? GetSimpleReference (Type type); + protected abstract IEnumerable GetSimpleReferences (Type type); [return: DynamicallyAccessedMembers (MethodsConstructors)] - public Type? GetType (JniTypeSignature typeSignature) - { - AssertValid (); + protected abstract Type? GetTypeForSimpleReference (string jniSimpleReference); + public abstract IEnumerable GetTypes (JniTypeSignature typeSignature); - return GetTypes (typeSignature).FirstOrDefault (); - } + public abstract IEnumerable GetReflectionConstructibleTypes (JniTypeSignature typeSignature); - public virtual IEnumerable GetTypes (JniTypeSignature typeSignature) + public class ReflectionConstructibleType { - AssertValid (); + public ReflectionConstructibleType ( + [DynamicallyAccessedMembers (Constructors)] + Type type) + { + Type = type; + } - if (typeSignature.SimpleReference == null) - return EmptyTypeArray; - return CreateGetTypesEnumerator (typeSignature); + [DynamicallyAccessedMembers (Constructors)] + public Type Type { get; } } - IEnumerable CreateGetTypesEnumerator (JniTypeSignature typeSignature) + protected abstract IEnumerable GetTypesForSimpleReference (string jniSimpleReference); + + static JniTypeSignature GetBuiltInTypeSignature (Type type) { - 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 = MakeGenericType (typeof (JavaObjectArray<>), arrayType); - } - yield return arrayType; - } - - if (typeSignature.ArrayRank > 0) { - var rank = typeSignature.ArrayRank; - var arrayType = type; - while (rank-- > 0) { - arrayType = MakeArrayType (arrayType); - } - yield return arrayType; - } - } + if (type == typeof (JavaProxyObject)) + return new JniTypeSignature (JavaProxyObject.JniTypeName, 0, false); + if (type == typeof (JavaProxyThrowable)) + return new JniTypeSignature (JavaProxyThrowable.JniTypeName, 0, false); + return default; } - IEnumerable GetPrimitiveArrayTypesForSimpleReference (JniTypeSignature typeSignature, Type type) + [return: DynamicallyAccessedMembers (MethodsConstructors)] + static Type? GetBuiltInType (JniTypeSignature typeSignature) { - 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 = MakeGenericType (typeof (JavaObjectArray<>), arrayType); - } - yield return arrayType; - - rank = typeSignature.ArrayRank-1; - arrayType = t; - while (rank-- > 0) { - arrayType = MakeArrayType (arrayType); - } - yield return arrayType; - } + 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, + }; } - protected virtual IEnumerable GetTypesForSimpleReference (string jniSimpleReference) - { - AssertValid (); - AssertSimpleReference (jniSimpleReference); + [return: DynamicallyAccessedMembers (MethodsConstructors)] + static Type TypeOf< + [DynamicallyAccessedMembers (MethodsConstructors)] + T> () => typeof (T); - // Not sure why CS8604 is reported on following line when we check against null ~9 lines above... - return CreateGetTypesForSimpleReferenceEnumerator (jniSimpleReference!); - } + [return: DynamicallyAccessedMembers (MethodsConstructors)] + static Type TypeOfVoid () => typeof (void); - IEnumerable CreateGetTypesForSimpleReferenceEnumerator (string jniSimpleReference) +#if NET + internal static bool TryRegisterBuiltInNativeMembers ( + JniType nativeClass, + string jniSimpleReference, + ReadOnlySpan methods) { - if (JniBuiltinSimpleReferenceToType.Value.TryGetValue (jniSimpleReference, out var ret)) { - yield return ret; - } - if (RuntimeFeature.ManagedPeerNativeRegistration && jniSimpleReference == ManagedPeer.JniTypeName) { - yield return typeof (ManagedPeer); + if (jniSimpleReference == JavaProxyObject.JniTypeName) { + var registrations = new List (); + JavaProxyObject.RegisterNativeMembers (new JniNativeMethodRegistrationArguments (registrations, null)); + if (registrations.Count > 0) + nativeClass.RegisterNativeMethods (registrations.ToArray ()); + return true; } - yield break; + + return jniSimpleReference == JavaProxyThrowable.JniTypeName && methods.IsEmpty; } +#endif /// [return: DynamicallyAccessedMembers (Constructors)] @@ -389,59 +277,30 @@ IEnumerable CreateGetTypesForSimpleReferenceEnumerator (string jniSimpleRe } return null; } - + [return: DynamicallyAccessedMembers (Constructors)] - 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); - } - + protected abstract Type? GetInvokerTypeCore ([DynamicallyAccessedMembers (Constructors)] Type type); #if NET + protected abstract IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimple); - public IReadOnlyList? GetStaticMethodFallbackTypes (string jniSimpleReference) + public string? GetReplacementType (string jniSimpleReference) { AssertValid (); AssertSimpleReference (jniSimpleReference, nameof (jniSimpleReference)); - return GetStaticMethodFallbackTypesCore (jniSimpleReference); + return GetReplacementTypeCore (jniSimpleReference); } - protected virtual IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimple) => null; + protected abstract string? GetReplacementTypeCore (string jniSimpleReference); - public string? GetReplacementType (string jniSimpleReference) + public IReadOnlyList? GetStaticMethodFallbackTypes (string jniSimpleReference) { AssertValid (); AssertSimpleReference (jniSimpleReference, nameof (jniSimpleReference)); - return GetReplacementTypeCore (jniSimpleReference); + return GetStaticMethodFallbackTypesCore (jniSimpleReference); } - protected virtual string? GetReplacementTypeCore (string jniSimpleReference) => null; - public ReplacementMethodInfo? GetReplacementMethodInfo (string jniSimpleReference, string jniMethodName, string jniMethodSignature) { AssertValid (); @@ -456,155 +315,22 @@ static Type MakeGenericType ( return GetReplacementMethodInfoCore (jniSimpleReference, jniMethodName, jniMethodSignature); } - protected virtual ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSimpleReference, string jniMethodName, string jniMethodSignature) => null; - - public virtual 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 (); + protected abstract ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSimpleReference, string jniMethodName, string jniMethodSignature); -#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 virtual void RegisterNativeMembers ( + public abstract void RegisterNativeMembers ( JniType nativeClass, [DynamicallyAccessedMembers (MethodsAndPrivateNested)] Type type, - string? methods) - { - TryRegisterNativeMembers (nativeClass, type, methods); - } - + ReadOnlySpan methods); +#endif #if NET [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan)")] #endif // NET - protected bool TryRegisterNativeMembers ( + public abstract void RegisterNativeMembers ( 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; - } + string? methods); } } } diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs index 4e39aa005..6ba8d5dae 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) { @@ -211,8 +141,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)); @@ -228,12 +157,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; @@ -264,37 +190,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; @@ -303,680 +204,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); - - [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; - } - } - return null; - } - } - - IJavaPeerable? TryCreatePeerInstance ( - ref JniObjectReference reference, - JniObjectReferenceOptions options, - [DynamicallyAccessedMembers (Constructors)] - Type type) - { - type = Runtime.TypeManager.GetInvokerType (type) ?? type; - - var self = GetUninitializedObject (type); - var constructed = false; - try { - constructed = TryConstructPeer (self, ref reference, options, type); - } finally { - if (!constructed) { - GC.SuppressFinalize (self); - self = null; - } - } - 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; - 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 ( + public T GetValue<[DynamicallyAccessedMembers (Constructors)] T> ( 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 (); + Type? targetType = null) + { + return GetValueCore (ref reference, options, targetType); + } - [return: MaybeNull] - public override IJavaPeerable? CreateGenericValue ( + [return: MaybeNull] + protected abstract T GetValueCore<[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: MaybeNull] - public override T CreateGenericValue ( + protected abstract object? GetValueCore ( 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); - } + Type? targetType = null); - [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); + [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.ReflectionJniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs new file mode 100644 index 000000000..5f1394ebb --- /dev/null +++ b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniTypeManager.cs @@ -0,0 +1,518 @@ +#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 ReflectionJniTypeManager : 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 () ?? throw new InvalidOperationException ("Array type has no element type."); + } + + 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 Type[] EmptyTypeArray = []; + + 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 MakeArrayType (Type type) => + TryMakeArrayType (type, out var arrayType) + ? arrayType ?? throw new InvalidOperationException ("Should not be reached") + : type.MakeArrayType (); + + 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) + { + AssertValid (); + AssertSimpleReference (jniSimpleReference); + + 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 (), + "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 = MakeJavaObjectArrayType (arrayType); + } + yield return arrayType; + } + + if (typeSignature.ArrayRank > 0) { + var rank = typeSignature.ArrayRank; + var arrayType = type; + while (rank-- > 0) { + arrayType = MakeArrayType (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 = MakeJavaObjectArrayType (arrayType); + } + yield return arrayType; + + rank = typeSignature.ArrayRank-1; + arrayType = t; + while (rank-- > 0) { + arrayType = MakeArrayType (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.ReflectionJniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs new file mode 100644 index 000000000..70ba47414 --- /dev/null +++ b/src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs @@ -0,0 +1,728 @@ +#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 ("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 ( + 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) + { + 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)); + + 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 = 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 { + + 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/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/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) diff --git a/src/Java.Interop/Java.Interop/ManagedPeer.cs b/src/Java.Interop/Java.Interop/ManagedPeer.cs index 56f1c22e7..cfd05c530 100644 --- a/src/Java.Interop/Java.Interop/ManagedPeer.cs +++ b/src/Java.Interop/Java.Interop/ManagedPeer.cs @@ -75,6 +75,7 @@ static void Construct ( Console.WriteLine ($"error: could not begin ManagedPeer.Construct!"); return; } + try { var runtime = JniEnvironment.Runtime; var r_self = new JniObjectReference (n_self); @@ -292,7 +293,6 @@ static unsafe void RegisterNativeMembers ( 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); diff --git a/src/Java.Interop/PublicAPI.Shipped.txt b/src/Java.Interop/PublicAPI.Shipped.txt index e1a948397..b03076199 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,21 +897,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 -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 -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.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.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 359f5560e..7dc728c0c 100644 --- a/src/Java.Interop/PublicAPI.Unshipped.txt +++ b/src/Java.Interop/PublicAPI.Unshipped.txt @@ -3,12 +3,25 @@ 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? -virtual Java.Interop.JniRuntime.JniValueManager.TryConstructPeer(Java.Interop.IJavaPeerable! self, ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type! type) -> bool +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! 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 +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 +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 @@ -35,4 +48,33 @@ 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? +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/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 128ddb626..b23de443c 100644 --- a/src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs +++ b/src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs @@ -9,7 +9,9 @@ namespace Java.Interop { - public class JreTypeManager : JniRuntime.JniTypeManager { + [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; @@ -33,6 +35,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/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 ece68972e..63063751f 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.Partial.cs @@ -1,4 +1,5 @@ -using System; +using System; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using Java.Interop; @@ -7,6 +8,8 @@ namespace Java.InteropTests { 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 (); @@ -25,4 +28,3 @@ protected JavaVMFixture () } } } - diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs index 40da2e321..0715b6a51 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,9 @@ static partial void CreateJavaVM () } } - class JavaVMFixtureTypeManager : JniRuntime.JniTypeManager { + [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() { #if !NO_MARSHAL_MEMBER_BUILDER_SUPPORT @@ -65,6 +68,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 +177,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..1994a073b 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs @@ -1,6 +1,4 @@ -using System; -using System.Reflection; -using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Java.Interop; @@ -12,6 +10,8 @@ namespace Java.InteropTests { public class JniRuntimeJniTypeManagerTests : JavaVMFixture { [Test] + [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,8 +26,12 @@ public void GetInvokerType () } } - class MyTypeManager : JniRuntime.JniTypeManager { + [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/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 3893f2c93..48b3e1663 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; @@ -26,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 { @@ -42,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; @@ -97,6 +102,8 @@ public JavaVMWithNullBuilder () } [Test] + [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; @@ -128,12 +135,16 @@ class JniProxyRuntime : JniRuntime { JniRuntime Proxy; + [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 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 { @@ -156,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) { @@ -189,7 +202,12 @@ public override void WaitForGCBridgeProcessing () } } - class ProxyTypeManager : JniTypeManager { + [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 () + { + } } } } diff --git a/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs b/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs index 3ecde892f..198cb9c97 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs @@ -246,4 +246,3 @@ class GenericHolder : JavaObject { public T Value {get; set;} } } -