From 4926f0c2225b2bf4df0d9dd6b26c8d030f2c4ecd Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Fri, 20 Dec 2024 07:47:46 -0500 Subject: [PATCH] [Mono.Android] Java.Interop Unification! MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Context: https://github.com/xamarin/monodroid/commit/e318861ed8eb20a71852378ddd558409d6b1c234 Context: 130905e1612f1c3f41b6b233056cae2753270630 Context: de043164416222ebf703f04205279fdcac56a9af Context: https://github.com/dotnet/android/pull/9636 In the beginning there was Mono for Android, which had a set of `Mono.Android.dll` assemblies (one per supported API level), each of which contained "duplicated" binding logic: each API level had its own `Java.Lang.Object`, `Android.Runtime.JNIEnv`, etc. dotnet/java-interop started, in part, as a way to "split out" the core integration logic, so that it *wouldn't* need to be duplicated across every assembly. As part of this, it introduced its own core abstractions, notably `Java.Interop.IJavaPeerable` and `Java.Interop.JavaObject`. When dotnet/java-interop was first introduced into Xamarin.Android, with xamarin/monodroid@e318861e, the integration was incomplete. Integration continued with 130905e1, allowing unit tests within `Java.Interop-Tests.dll` to run within Xamarin.Android and construction of instances of e.g. `JavaInt32Array`, but one large piece of integration remained: Moving GC bridge code *out* of `Java.Lang.Object`, and instead relying on `Java.Interop.JavaObject`, turning this: namespace Java.Lang { public partial class Object : System.Object, IJavaPeerable /* … */ { } } into this: namespace Java.Lang { public partial class Object : Java.Interop.JavaObject, IJavaPeerable /* … */ { } } *Why*? In part because @jonpryor has wanted to do this for literal years at this point, but also in part because of dotnet/android#9636 and related efforts to use Native AOT, which involves avoiding / bypassing `DllImportAttribute` invocations (for now, everything touched by Native AOT becomes a single `.so` binary, which we don't know the name of). Avoiding P/Invoke means *embracing* and extending existing Java.Interop constructs (e.g. de043164). In addition to altering the base types of `Java.Lang.Object` and `Java.Lang.Throwable`: * Remove `handle` and related fields from `Java.Lang.Object` and `Java.Lang.Throwable`. * Update `PreserveLists/Mono.Android.xml` so that the removed fields are note preserved. * Rename `JNIenvInit.AndroidValueManager` to `JNIEnvInit.ValueManager`, and change its type to `JniRuntime.JniValueManager`. This is to help "force" usage of `JnIRuntime.JniValueManager` in more places, as we can't currently use `AndroidValueManager` in Native AOT (P/Invokes!). * Cleanup: Remove `JNIEnv.Exit()` and related code. These were used by the Android Designer, which is no longer supported. * Update (`internal`) interface `IJavaObjectEx` to remove constructs present on `IJavaPeerable.` Known issues: * `Java.Lang.Throwable(IntPtr, JniHandleOwnership)` invocation will result in construction of an "extra" `java.lang.Throwable` instance on the Java side, which will be immediately discarded. This is because it uses the `JavaException(string, Exception)` constructor, which implicitly creates a Java peer. We may need dotnet/java-interop changes to better address this. --- .../PreserveLists/Mono.Android.xml | 8 - .../Android.Runtime/AndroidRuntime.cs | 18 +- src/Mono.Android/Android.Runtime/JNIEnv.cs | 43 ---- .../Android.Runtime/JNIEnvInit.cs | 4 +- .../Java.Interop/IJavaObjectEx.cs | 3 - src/Mono.Android/Java.Interop/Runtime.cs | 2 +- src/Mono.Android/Java.Interop/TypeManager.cs | 13 +- src/Mono.Android/Java.Lang/Object.cs | 182 +++------------ src/Mono.Android/Java.Lang/Throwable.cs | 210 +++--------------- .../PublicAPI/API-35/PublicAPI.Shipped.txt | 8 +- .../PublicAPI/API-36/PublicAPI.Shipped.txt | 8 +- src/native/monodroid/osbridge.cc | 2 - 12 files changed, 91 insertions(+), 410 deletions(-) diff --git a/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml b/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml index fa71749e460..e14e67740fe 100644 --- a/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml +++ b/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml @@ -36,15 +36,7 @@ --> - - - - - - - - diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs index e6de9e23399..182a69695fc 100644 --- a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs +++ b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs @@ -56,7 +56,7 @@ public override string GetCurrentManagedThreadStackTrace (int skipFrames, bool f { if (!reference.IsValid) return null; - var peeked = JNIEnvInit.AndroidValueManager?.PeekPeer (reference); + var peeked = JNIEnvInit.ValueManager?.PeekPeer (reference); var peekedExc = peeked as Exception; if (peekedExc == null) { var throwable = Java.Lang.Object.GetObject (reference.Handle, JniHandleOwnership.DoNotTransfer); @@ -64,21 +64,28 @@ public override string GetCurrentManagedThreadStackTrace (int skipFrames, bool f return throwable; } JniObjectReference.Dispose (ref reference, options); - var unwrapped = JNIEnvInit.AndroidValueManager?.UnboxException (peeked!); + var unwrapped = UnboxException (peeked!); if (unwrapped != null) { return unwrapped; } return peekedExc; } + Exception? UnboxException (IJavaPeerable value) + { + if (JNIEnvInit.ValueManager is AndroidValueManager vm) { + return vm.UnboxException (value); + } + return null; + } + public override void RaisePendingException (Exception pendingException) { - var je = pendingException as JavaProxyThrowable; + var je = pendingException as JavaException; if (je == null) { je = JavaProxyThrowable.Create (pendingException); } - var r = new JniObjectReference (je.Handle); - JniEnvironment.Exceptions.Throw (r); + JniEnvironment.Exceptions.Throw (je.PeerReference); } } @@ -470,6 +477,7 @@ static bool CallRegisterMethodByIndex (JniNativeMethodRegistrationArguments argu } } + [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan) instead.")] public override void RegisterNativeMembers ( JniType nativeClass, [DynamicallyAccessedMembers (MethodsAndPrivateNested)] diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs index 9d6ba857379..42a3553adbf 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnv.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs @@ -76,49 +76,6 @@ internal static bool ShouldWrapJavaException (Java.Lang.Throwable? t, [CallerMem return wrap; } - [DllImport ("libc")] - static extern int gettid (); - - internal static void Exit () - { - /* Manually dispose surfaced objects and close the current JniEnvironment to - * avoid ObjectDisposedException thrown on finalizer threads after shutdown - */ - foreach (var surfacedObject in Java.Interop.Runtime.GetSurfacedObjects ()) { - try { - var obj = surfacedObject.Target as IDisposable; - if (obj != null) - obj.Dispose (); - continue; - } catch (Exception e) { - RuntimeNativeMethods.monodroid_log (LogLevel.Warn, LogCategories.Default, $"Couldn't dispose object: {e}"); - } - /* If calling Dispose failed, the assumption is that user-code in - * the Dispose(bool) overload is to blame for it. In that case we - * fallback to manual deletion of the surfaced object. - */ - var jobj = surfacedObject.Target as Java.Lang.Object; - if (jobj != null) - ManualJavaObjectDispose (jobj); - } - JniEnvironment.Runtime.Dispose (); - } - - /* FIXME: This reproduces the minimal steps in Java.Lang.Object.Dispose - * that needs to be executed so that we don't leak any GREF and prevent - * code execution into an appdomain that we are disposing via a finalizer. - * Ideally it should be done via another more generic mechanism, likely - * from the Java.Interop.Runtime API. - */ - static void ManualJavaObjectDispose (Java.Lang.Object obj) - { - var peer = obj.PeerReference; - var handle = peer.Handle; - var keyHandle = ((IJavaObjectEx)obj).KeyHandle; - Java.Lang.Object.Dispose (obj, ref handle, keyHandle, (JObjectRefType)peer.Type); - GC.SuppressFinalize (obj); - } - internal static void PropagateUncaughtException (IntPtr env, IntPtr javaThreadPtr, IntPtr javaExceptionPtr) { if (!JNIEnvInit.PropagateExceptions) diff --git a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs index c942c6c3f43..5efac42f42b 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs @@ -35,7 +35,7 @@ internal struct JnienvInitializeArgs { } #pragma warning restore 0649 - internal static AndroidValueManager? AndroidValueManager; + internal static JniRuntime.JniValueManager? ValueManager; internal static bool IsRunningOnDesktop; internal static bool jniRemappingInUse; internal static bool MarshalMethodsEnabled; @@ -94,7 +94,7 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) BoundExceptionType = (BoundExceptionType)args->ioExceptionType; androidRuntime = new AndroidRuntime (args->env, args->javaVm, args->grefLoader, args->Loader_loadClass, args->jniAddNativeMethodRegistrationAttributePresent != 0); - AndroidValueManager = (AndroidValueManager) androidRuntime.ValueManager; + ValueManager = androidRuntime.ValueManager; IsRunningOnDesktop = args->isRunningOnDesktop == 1; diff --git a/src/Mono.Android/Java.Interop/IJavaObjectEx.cs b/src/Mono.Android/Java.Interop/IJavaObjectEx.cs index 4e09670fc9c..e66229d1b9b 100644 --- a/src/Mono.Android/Java.Interop/IJavaObjectEx.cs +++ b/src/Mono.Android/Java.Interop/IJavaObjectEx.cs @@ -3,9 +3,6 @@ namespace Java.Interop { interface IJavaObjectEx { - IntPtr KeyHandle {get; set;} - bool IsProxy {get; set;} - bool NeedsActivation {get; set;} IntPtr ToLocalJniHandle (); } } diff --git a/src/Mono.Android/Java.Interop/Runtime.cs b/src/Mono.Android/Java.Interop/Runtime.cs index ceb14ca9d87..8d69aedffa4 100644 --- a/src/Mono.Android/Java.Interop/Runtime.cs +++ b/src/Mono.Android/Java.Interop/Runtime.cs @@ -11,7 +11,7 @@ public static class Runtime { [Obsolete ("Please use Java.Interop.JniEnvironment.Runtime.ValueManager.GetSurfacedPeers()")] public static List GetSurfacedObjects () { - var peers = JNIEnvInit.AndroidValueManager!.GetSurfacedPeers (); + var peers = JNIEnvInit.ValueManager!.GetSurfacedPeers (); var r = new List (peers.Count); foreach (var p in peers) { if (p.SurfacedPeer.TryGetTarget (out var target)) diff --git a/src/Mono.Android/Java.Interop/TypeManager.cs b/src/Mono.Android/Java.Interop/TypeManager.cs index 04aa1f0ed5a..55a040f7131 100644 --- a/src/Mono.Android/Java.Interop/TypeManager.cs +++ b/src/Mono.Android/Java.Interop/TypeManager.cs @@ -133,9 +133,10 @@ static Type[] GetParameterTypes (string? signature) static void n_Activate (IntPtr jnienv, IntPtr jclass, IntPtr typename_ptr, IntPtr signature_ptr, IntPtr jobject, IntPtr parameters_ptr) { var o = Java.Lang.Object.PeekObject (jobject); - var ex = o as IJavaObjectEx; + var ex = o as IJavaPeerable; if (ex != null) { - if (!ex.NeedsActivation && !ex.IsProxy) + var state = ex.JniManagedPeerState; + if (!state.HasFlag (JniManagedPeerStates.Activatable) && !state.HasFlag (JniManagedPeerStates.Replaceable)) return; } if (!ActivationEnabled) { @@ -171,10 +172,8 @@ internal static void Activate (IntPtr jobject, ConstructorInfo cinfo, object? [] { try { var newobj = RuntimeHelpers.GetUninitializedObject (cinfo.DeclaringType!); - if (newobj is Java.Lang.Object o) { - o.handle = jobject; - } else if (newobj is Java.Lang.Throwable throwable) { - throwable.handle = jobject; + if (newobj is IJavaPeerable peer) { + peer.SetPeerReference (new JniObjectReference (jobject)); } else { throw new InvalidOperationException ($"Unsupported type: '{newobj}'"); } @@ -232,7 +231,7 @@ static Exception CreateJavaLocationException () return null; } - internal static IJavaPeerable CreateInstance (IntPtr handle, JniHandleOwnership transfer) + internal static IJavaPeerable? CreateInstance (IntPtr handle, JniHandleOwnership transfer) { return CreateInstance (handle, transfer, null); } diff --git a/src/Mono.Android/Java.Lang/Object.cs b/src/Mono.Android/Java.Lang/Object.cs index 9df5b5bbee9..d45c6d295d3 100644 --- a/src/Mono.Android/Java.Lang/Object.cs +++ b/src/Mono.Android/Java.Lang/Object.cs @@ -14,64 +14,31 @@ namespace Java.Lang { [Serializable] - public partial class Object : IDisposable, IJavaObject, IJavaObjectEx -#if JAVA_INTEROP - , IJavaPeerable -#endif // JAVA_INTEROP + public partial class Object : global::Java.Interop.JavaObject, IJavaObject, IJavaObjectEx { - [NonSerialized] IntPtr key_handle; -#pragma warning disable CS0649, CS0169, CS0414 // Suppress fields are never used warnings, these fields are used directly by monodroid-glue.cc - [NonSerialized] int refs_added; -#pragma warning restore CS0649, CS0169, CS0414 - [NonSerialized] JObjectRefType handle_type; - [NonSerialized] internal IntPtr handle; - [NonSerialized] bool needsActivation; - [NonSerialized] bool isProxy; - - IntPtr IJavaObjectEx.KeyHandle { - get {return key_handle;} - set {key_handle = value;} - } - - bool IJavaObjectEx.IsProxy { - get {return isProxy;} - set {isProxy = value;} - } - - bool IJavaObjectEx.NeedsActivation { - get {return needsActivation;} - set {needsActivation = true;} - } - IntPtr IJavaObjectEx.ToLocalJniHandle () { lock (this) { - if (handle == IntPtr.Zero) - return handle; - return JNIEnv.NewLocalRef (handle); + var peerRef = PeerReference; + if (!peerRef.IsValid) + return IntPtr.Zero; + return peerRef.NewLocalRef ().Handle; } } ~Object () { - // FIXME: need hash cleanup mechanism. - // Finalization occurs after a test of java persistence. If the - // handle still contains a java reference, we can't finalize the - // object and should "resurrect" it. - refs_added = 0; - if (Environment.HasShutdownStarted) { - return; - } - JniEnvironment.Runtime.ValueManager.FinalizePeer (this); } - public Object (IntPtr handle, JniHandleOwnership transfer) + public unsafe Object (IntPtr handle, JniHandleOwnership transfer) + : base (ref *InvalidJniObjectReference, JniObjectReferenceOptions.None) { // Check if handle was preset by our java activation mechanism - if (this.handle != IntPtr.Zero) { - needsActivation = true; - handle = this.handle; - if (handle_type != 0) + var peerRef = PeerReference; + if (peerRef.IsValid) { + handle = peerRef.Handle; + ((IJavaPeerable) this).SetJniManagedPeerState (JniManagedPeerStates.Activatable); + if (peerRef.Type != JniObjectReferenceType.Invalid) return; transfer = JniHandleOwnership.DoNotTransfer; } @@ -92,31 +59,26 @@ internal void SetHandleOnDeserialized (StreamingContext context) JNIEnv.FinishCreateInstance (Handle, "()V"); } -#if JAVA_INTEROP [EditorBrowsable (EditorBrowsableState.Never)] - public int JniIdentityHashCode { - get {return (int) key_handle;} - } + public int JniIdentityHashCode => base.JniIdentityHashCode; [DebuggerBrowsable (DebuggerBrowsableState.Never)] [EditorBrowsable (EditorBrowsableState.Never)] - public JniObjectReference PeerReference { - get { - return new JniObjectReference (handle, (JniObjectReferenceType) handle_type); - } - } + public JniObjectReference PeerReference => base.PeerReference; [DebuggerBrowsable (DebuggerBrowsableState.Never)] [EditorBrowsable (EditorBrowsableState.Never)] - public virtual JniPeerMembers JniPeerMembers { + public override JniPeerMembers JniPeerMembers { get { return _members; } } -#endif // JAVA_INTEROP [EditorBrowsable (EditorBrowsableState.Never)] public IntPtr Handle { get { - return handle; + var peerRef = PeerReference; + if (!peerRef.IsValid) + return IntPtr.Zero; + return peerRef.Handle; } } @@ -142,115 +104,29 @@ internal System.Type GetThresholdType () return ThresholdType; } -#if JAVA_INTEROP - JniManagedPeerStates IJavaPeerable.JniManagedPeerState { - get { - var e = (IJavaObjectEx) this; - var s = JniManagedPeerStates.None; - if (e.IsProxy) - s |= JniManagedPeerStates.Replaceable; - if (e.NeedsActivation) - s |= JniManagedPeerStates.Activatable; - return s; - } - } - - void IJavaPeerable.DisposeUnlessReferenced () - { - var p = PeekObject (handle); - if (p == null) { - Dispose (); - } - } - [EditorBrowsable (EditorBrowsableState.Never)] - public void UnregisterFromRuntime () - { - JNIEnvInit.AndroidValueManager?.RemovePeer (this, key_handle); - } - - void IJavaPeerable.Disposed () - { - Dispose (disposing: true); - } - - void IJavaPeerable.Finalized () - { - Dispose (disposing: false); - } - - void IJavaPeerable.SetJniIdentityHashCode (int value) - { - key_handle = (IntPtr) value; - } + public new void UnregisterFromRuntime () => base.UnregisterFromRuntime (); - void IJavaPeerable.SetJniManagedPeerState (JniManagedPeerStates value) + protected override void Dispose (bool disposing) { - var e = (IJavaObjectEx) this; - if ((value & JniManagedPeerStates.Replaceable) == JniManagedPeerStates.Replaceable) - e.IsProxy = true; - if ((value & JniManagedPeerStates.Activatable) == JniManagedPeerStates.Activatable) - e.NeedsActivation = true; } - void IJavaPeerable.SetPeerReference (JniObjectReference reference) - { - this.handle = reference.Handle; - this.handle_type = (JObjectRefType) reference.Type; - } -#endif // JAVA_INTEROP - - - public void Dispose () - { - JNIEnvInit.AndroidValueManager?.DisposePeer (this); - } - - protected virtual void Dispose (bool disposing) - { - } - - internal static void Dispose (IJavaPeerable instance, ref IntPtr handle, IntPtr key_handle, JObjectRefType handle_type) - { - if (handle == IntPtr.Zero) - return; - - if (Logger.LogGlobalRef) { - RuntimeNativeMethods._monodroid_gref_log ( - FormattableString.Invariant ($"Disposing handle 0x{handle:x}\n")); - } - - JNIEnvInit.AndroidValueManager?.RemovePeer (instance, key_handle); - - switch (handle_type) { - case JObjectRefType.Global: - lock (instance) { - JNIEnv.DeleteGlobalRef (handle); - handle = IntPtr.Zero; - } - break; - case JObjectRefType.WeakGlobal: - lock (instance) { - JNIEnv.DeleteWeakGlobalRef (handle); - handle = IntPtr.Zero; - } - break; - default: - throw new InvalidOperationException ("Trying to dispose handle of type '" + - handle_type + "' which is not supported."); - } - } + public new void Dispose () => base.Dispose (); [EditorBrowsable (EditorBrowsableState.Never)] protected void SetHandle (IntPtr value, JniHandleOwnership transfer) { - JNIEnvInit.AndroidValueManager?.AddPeer (this, value, transfer, out handle); - handle_type = JObjectRefType.Global; + var reference = new JniObjectReference (value); + JNIEnvInit.ValueManager?.ConstructPeer ( + this, + ref reference, + value == IntPtr.Zero ? JniObjectReferenceOptions.None : JniObjectReferenceOptions.Copy); + JNIEnv.DeleteRef (value, transfer); } internal static IJavaPeerable? PeekObject (IntPtr handle, Type? requiredType = null) { - var peeked = JNIEnvInit.AndroidValueManager?.PeekPeer (new JniObjectReference (handle)); + var peeked = JNIEnvInit.ValueManager?.PeekPeer (new JniObjectReference (handle)); if (peeked == null) return null; if (requiredType != null && !requiredType.IsAssignableFrom (peeked.GetType ())) diff --git a/src/Mono.Android/Java.Lang/Throwable.cs b/src/Mono.Android/Java.Lang/Throwable.cs index 498335e5d8e..b12e5631ffb 100644 --- a/src/Mono.Android/Java.Lang/Throwable.cs +++ b/src/Mono.Android/Java.Lang/Throwable.cs @@ -9,23 +9,9 @@ namespace Java.Lang { - public partial class Throwable : global::System.Exception, IJavaObject, IDisposable, IJavaObjectEx -#if JAVA_INTEROP - , IJavaPeerable -#endif // JAVA_INTEROP + public partial class Throwable : JavaException, IJavaObject, IDisposable, IJavaObjectEx { - protected bool is_generated; - internal IntPtr handle; - - IntPtr key_handle; - JObjectRefType handle_type; -#pragma warning disable CS0649, CS0169, CS0414 // Suppress fields are never used warnings, these fields are used directly by monodroid-glue.cc - int refs_added; -#pragma warning restore CS0649, CS0169, CS0414 - - bool isProxy; - bool needsActivation; string? nativeStack; @@ -36,10 +22,11 @@ public Throwable (IntPtr handle, JniHandleOwnership transfer) is_generated = true; // Check if handle was preset by our java activation mechanism - if (this.handle != IntPtr.Zero) { - needsActivation = true; - handle = this.handle; - if (handle_type != 0) + var peerRef = PeerReference; + if (peerRef.IsValid) { + ((IJavaPeerable) this).SetJniManagedPeerState (JniManagedPeerStates.Activatable); + handle = peerRef.Handle; + if (peerRef.Type != JniObjectReferenceType.Invalid) return; transfer = JniHandleOwnership.DoNotTransfer; } @@ -57,24 +44,16 @@ public Throwable (IntPtr handle, JniHandleOwnership transfer) return null; IntPtr value; -#if JAVA_INTEROP const string __id = "getMessage.()Ljava/lang/String;"; if (Throwable_getMessage == null) { Throwable_getMessage = _members.InstanceMethods.GetMethodInfo (__id); } value = JniEnvironment.InstanceMethods.CallObjectMethod (new JniObjectReference (handle), Throwable_getMessage).Handle; -#else // !JAVA_INTEROP - if (id_getMessage == IntPtr.Zero) - id_getMessage = JNIEnv.GetMethodID (class_ref, "getMessage", "()Ljava/lang/String;"); - value = JNIEnv.CallObjectMethod (handle, id_getMessage); -#endif // !JAVA_INTEROP return JNIEnv.GetString (value, JniHandleOwnership.TransferLocalRef); } -#if JAVA_INTEROP static JniMethodInfo? Throwable_getCause; -#endif static global::System.Exception? _GetInnerException (IntPtr handle) { @@ -82,18 +61,11 @@ public Throwable (IntPtr handle, JniHandleOwnership transfer) return null; IntPtr value; -#if JAVA_INTEROP const string __id = "getCause.()Ljava/lang/Throwable;"; if (Throwable_getCause == null) { Throwable_getCause = _members.InstanceMethods.GetMethodInfo (__id); } value = JniEnvironment.InstanceMethods.CallObjectMethod (new JniObjectReference (handle), Throwable_getCause).Handle; -#else // !JAVA_INTEROP - if (id_getCause == IntPtr.Zero) - id_getCause = JNIEnv.GetMethodID (class_ref, "getCause", "()Ljava/lang/Throwable;"); - - value = JNIEnv.CallObjectMethod (handle, id_getCause); -#endif // !JAVA_INTEROP var cause = global::Java.Lang.Object.GetObject ( value, @@ -106,94 +78,45 @@ public Throwable (IntPtr handle, JniHandleOwnership transfer) return cause; } - IntPtr IJavaObjectEx.KeyHandle { - get {return key_handle;} - set {key_handle = value;} - } - - bool IJavaObjectEx.IsProxy { - get {return isProxy;} - set {isProxy = value;} - } - - bool IJavaObjectEx.NeedsActivation { - get {return needsActivation;} - set {needsActivation = true;} - } - IntPtr IJavaObjectEx.ToLocalJniHandle () { lock (this) { - if (handle == IntPtr.Zero) - return handle; - return JNIEnv.NewLocalRef (handle); + var peerRef = PeerReference; + if (!peerRef.IsValid) + return IntPtr.Zero; + return peerRef.NewLocalRef ().Handle; } } - public override string StackTrace { - get { - return base.StackTrace + ManagedStackTraceAddendum; - } - } - - string ManagedStackTraceAddendum { - get { - var javaStack = JavaStackTrace; - if (string.IsNullOrEmpty (javaStack)) - return ""; - return Environment.NewLine + - " --- End of managed " + - GetType ().FullName + - " stack trace ---" + Environment.NewLine + - javaStack; - } - } - - string? JavaStackTrace { - get { - if (!string.IsNullOrEmpty (nativeStack)) - return nativeStack; - - if (handle == IntPtr.Zero) - return null; - - using (var nativeStackWriter = new Java.IO.StringWriter ()) - using (var nativeStackPw = new Java.IO.PrintWriter (nativeStackWriter)) { - PrintStackTrace (nativeStackPw); - nativeStack = nativeStackWriter.ToString (); - } - return nativeStack; - } - } + public override string StackTrace => base.StackTrace; public override string ToString () { - return base.ToString () + ManagedStackTraceAddendum; + return base.ToString (); } -#if JAVA_INTEROP [EditorBrowsable (EditorBrowsableState.Never)] - public int JniIdentityHashCode { - get {return (int) key_handle;} - } + public int JniIdentityHashCode => base.JniIdentityHashCode; [DebuggerBrowsable (DebuggerBrowsableState.Never)] [EditorBrowsable (EditorBrowsableState.Never)] - public JniObjectReference PeerReference { - get { - return new JniObjectReference (handle, (JniObjectReferenceType) handle_type); - } - } + public JniObjectReference PeerReference => base.PeerReference; [DebuggerBrowsable (DebuggerBrowsableState.Never)] [EditorBrowsable (EditorBrowsableState.Never)] - public virtual JniPeerMembers JniPeerMembers { + public override JniPeerMembers JniPeerMembers { get { return _members; } } -#endif // JAVA_INTEROP [EditorBrowsable (EditorBrowsableState.Never)] - public IntPtr Handle { get { return handle; } } + public IntPtr Handle { + get { + var peerRef = PeerReference; + if (!peerRef.IsValid) + return IntPtr.Zero; + return peerRef.Handle; + } + } [DebuggerBrowsable (DebuggerBrowsableState.Never)] [EditorBrowsable (EditorBrowsableState.Never)] @@ -217,22 +140,12 @@ internal System.Type GetThresholdType () return ThresholdType; } -#if !JAVA_INTEROP - static IntPtr id_getClass; -#endif // !JAVA_INTEROP - public unsafe Java.Lang.Class? Class { [Register ("getClass", "()Ljava/lang/Class;", "GetGetClassHandler")] get { IntPtr value; -#if JAVA_INTEROP const string __id = "getClass.()Ljava/lang/Class;"; value = _members.InstanceMethods.InvokeVirtualObjectMethod (__id, this, null).Handle; -#else // !JAVA_INTEROP - if (id_getClass == IntPtr.Zero) - id_getClass = JNIEnv.GetMethodID (class_ref, "getClass", "()Ljava/lang/Class;"); - value = JNIEnv.CallObjectMethod (Handle, id_getClass); -#endif // !JAVA_INTEROP return global::Java.Lang.Object.GetObject (value, JniHandleOwnership.TransferLocalRef); } } @@ -240,8 +153,12 @@ public unsafe Java.Lang.Class? Class { [EditorBrowsable (EditorBrowsableState.Never)] protected void SetHandle (IntPtr value, JniHandleOwnership transfer) { - JNIEnvInit.AndroidValueManager?.AddPeer (this, value, transfer, out handle); - handle_type = JObjectRefType.Global; + var reference = new JniObjectReference (value); + JNIEnvInit.ValueManager?.ConstructPeer ( + this, + ref reference, + value == IntPtr.Zero ? JniObjectReferenceOptions.None : JniObjectReferenceOptions.Copy); + JNIEnv.DeleteRef (value, transfer); } public static Throwable FromException (System.Exception e) @@ -265,76 +182,13 @@ public static System.Exception ToException (Throwable e) ~Throwable () { - refs_added = 0; - if (Environment.HasShutdownStarted) { - return; - } - JniEnvironment.Runtime.ValueManager.FinalizePeer (this); - } - -#if JAVA_INTEROP - JniManagedPeerStates IJavaPeerable.JniManagedPeerState { - get { - var e = (IJavaObjectEx) this; - var s = JniManagedPeerStates.None; - if (e.IsProxy) - s |= JniManagedPeerStates.Replaceable; - if (e.NeedsActivation) - s |= JniManagedPeerStates.Activatable; - return s; - } - } - - void IJavaPeerable.DisposeUnlessReferenced () - { - var p = Object.PeekObject (handle); - if (p == null) { - Dispose (); - } - } - - public void UnregisterFromRuntime () - { - JNIEnvInit.AndroidValueManager?.RemovePeer (this, key_handle); } - void IJavaPeerable.Disposed () - { - Dispose (disposing: true); - } - - void IJavaPeerable.Finalized () - { - Dispose (disposing: false); - } - - void IJavaPeerable.SetJniIdentityHashCode (int value) - { - key_handle = (IntPtr) value; - } + public void UnregisterFromRuntime () => base.UnregisterFromRuntime (); - void IJavaPeerable.SetJniManagedPeerState (JniManagedPeerStates value) - { - var e = (IJavaObjectEx) this; - if ((value & JniManagedPeerStates.Replaceable) == JniManagedPeerStates.Replaceable) - e.IsProxy = true; - if ((value & JniManagedPeerStates.Activatable) == JniManagedPeerStates.Activatable) - e.NeedsActivation = true; - } - - void IJavaPeerable.SetPeerReference (JniObjectReference reference) - { - this.handle = reference.Handle; - this.handle_type = (JObjectRefType) reference.Type; - } -#endif // JAVA_INTEROP - - public void Dispose () - { - JNIEnvInit.AndroidValueManager?.DisposePeer (this); - } + public void Dispose () => base.Dispose (); - protected virtual void Dispose (bool disposing) + protected override void Dispose (bool disposing) { } } diff --git a/src/Mono.Android/PublicAPI/API-35/PublicAPI.Shipped.txt b/src/Mono.Android/PublicAPI/API-35/PublicAPI.Shipped.txt index a015c8c4a8f..d71147178cd 100644 --- a/src/Mono.Android/PublicAPI/API-35/PublicAPI.Shipped.txt +++ b/src/Mono.Android/PublicAPI/API-35/PublicAPI.Shipped.txt @@ -125925,10 +125925,10 @@ virtual Java.Lang.NoSuchMethodException.Clause.get -> Java.Lang.Throwable? virtual Java.Lang.Number.ByteValue() -> sbyte virtual Java.Lang.Number.ShortValue() -> short virtual Java.Lang.Object.Clone() -> Java.Lang.Object! -virtual Java.Lang.Object.Dispose(bool disposing) -> void +override Java.Lang.Object.Dispose(bool disposing) -> void virtual Java.Lang.Object.Equals(Java.Lang.Object? obj) -> bool virtual Java.Lang.Object.JavaFinalize() -> void -virtual Java.Lang.Object.JniPeerMembers.get -> Java.Interop.JniPeerMembers! +override Java.Lang.Object.JniPeerMembers.get -> Java.Interop.JniPeerMembers! virtual Java.Lang.Object.ThresholdClass.get -> nint virtual Java.Lang.Object.ThresholdType.get -> System.Type! virtual Java.Lang.Package.GetAnnotation(Java.Lang.Class? annotationClass) -> Java.Lang.Object? @@ -126069,11 +126069,11 @@ virtual Java.Lang.ThreadLocal.InitialValue() -> Java.Lang.Object? virtual Java.Lang.ThreadLocal.Remove() -> void virtual Java.Lang.ThreadLocal.Set(Java.Lang.Object? value) -> void virtual Java.Lang.Throwable.Cause.get -> Java.Lang.Throwable? -virtual Java.Lang.Throwable.Dispose(bool disposing) -> void +override Java.Lang.Throwable.Dispose(bool disposing) -> void virtual Java.Lang.Throwable.FillInStackTrace() -> Java.Lang.Throwable! virtual Java.Lang.Throwable.GetStackTrace() -> Java.Lang.StackTraceElement![]! virtual Java.Lang.Throwable.InitCause(Java.Lang.Throwable? cause) -> Java.Lang.Throwable! -virtual Java.Lang.Throwable.JniPeerMembers.get -> Java.Interop.JniPeerMembers! +override Java.Lang.Throwable.JniPeerMembers.get -> Java.Interop.JniPeerMembers! virtual Java.Lang.Throwable.LocalizedMessage.get -> string? virtual Java.Lang.Throwable.Message.get -> string? virtual Java.Lang.Throwable.PrintStackTrace() -> void diff --git a/src/Mono.Android/PublicAPI/API-36/PublicAPI.Shipped.txt b/src/Mono.Android/PublicAPI/API-36/PublicAPI.Shipped.txt index 4cfc5b60105..f2e7bfa0200 100644 --- a/src/Mono.Android/PublicAPI/API-36/PublicAPI.Shipped.txt +++ b/src/Mono.Android/PublicAPI/API-36/PublicAPI.Shipped.txt @@ -125925,10 +125925,10 @@ virtual Java.Lang.NoSuchMethodException.Clause.get -> Java.Lang.Throwable? virtual Java.Lang.Number.ByteValue() -> sbyte virtual Java.Lang.Number.ShortValue() -> short virtual Java.Lang.Object.Clone() -> Java.Lang.Object! -virtual Java.Lang.Object.Dispose(bool disposing) -> void +override Java.Lang.Object.Dispose(bool disposing) -> void virtual Java.Lang.Object.Equals(Java.Lang.Object? obj) -> bool virtual Java.Lang.Object.JavaFinalize() -> void -virtual Java.Lang.Object.JniPeerMembers.get -> Java.Interop.JniPeerMembers! +override Java.Lang.Object.JniPeerMembers.get -> Java.Interop.JniPeerMembers! virtual Java.Lang.Object.ThresholdClass.get -> nint virtual Java.Lang.Object.ThresholdType.get -> System.Type! virtual Java.Lang.Package.GetAnnotation(Java.Lang.Class? annotationClass) -> Java.Lang.Object? @@ -126069,11 +126069,11 @@ virtual Java.Lang.ThreadLocal.InitialValue() -> Java.Lang.Object? virtual Java.Lang.ThreadLocal.Remove() -> void virtual Java.Lang.ThreadLocal.Set(Java.Lang.Object? value) -> void virtual Java.Lang.Throwable.Cause.get -> Java.Lang.Throwable? -virtual Java.Lang.Throwable.Dispose(bool disposing) -> void +override Java.Lang.Throwable.Dispose(bool disposing) -> void virtual Java.Lang.Throwable.FillInStackTrace() -> Java.Lang.Throwable! virtual Java.Lang.Throwable.GetStackTrace() -> Java.Lang.StackTraceElement![]! virtual Java.Lang.Throwable.InitCause(Java.Lang.Throwable? cause) -> Java.Lang.Throwable! -virtual Java.Lang.Throwable.JniPeerMembers.get -> Java.Interop.JniPeerMembers! +override Java.Lang.Throwable.JniPeerMembers.get -> Java.Interop.JniPeerMembers! virtual Java.Lang.Throwable.LocalizedMessage.get -> string? virtual Java.Lang.Throwable.Message.get -> string? virtual Java.Lang.Throwable.PrintStackTrace() -> void diff --git a/src/native/monodroid/osbridge.cc b/src/native/monodroid/osbridge.cc index d2725d278e2..1ae581d001e 100644 --- a/src/native/monodroid/osbridge.cc +++ b/src/native/monodroid/osbridge.cc @@ -23,8 +23,6 @@ using namespace xamarin::android; using namespace xamarin::android::internal; const OSBridge::MonoJavaGCBridgeType OSBridge::mono_xa_gc_bridge_types[] = { - { "Java.Lang", "Object" }, - { "Java.Lang", "Throwable" }, }; const OSBridge::MonoJavaGCBridgeType OSBridge::mono_ji_gc_bridge_types[] = {