Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Mono.Android] Java.Interop Unification!
Browse files Browse the repository at this point in the history
Context: xamarin/monodroid@e318861
Context: 130905e
Context: de04316
Context: #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 130905e, 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 #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. de04316).

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.
jonpryor committed Dec 20, 2024
1 parent f83583b commit 4926f0c
Showing 12 changed files with 91 additions and 410 deletions.
Original file line number Diff line number Diff line change
@@ -36,15 +36,7 @@
-->
<type fullname="Java.Interop.TypeManager*JavaTypeManager" />
<type fullname="Java.Lang.Object">
<field name="handle" />
<field name="handle_type" />
<field name="refs_added" />
<method name="SetHandleOnDeserialized" />
</type>
<type fullname="Java.Lang.Throwable">
<field name="handle" />
<field name="handle_type" />
<field name="refs_added" />
</type>
</assembly>
</linker>
18 changes: 13 additions & 5 deletions src/Mono.Android/Android.Runtime/AndroidRuntime.cs
Original file line number Diff line number Diff line change
@@ -56,29 +56,36 @@ 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<Java.Lang.Throwable> (reference.Handle, JniHandleOwnership.DoNotTransfer);
JniObjectReference.Dispose (ref reference, options);
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<char>) instead.")]
public override void RegisterNativeMembers (
JniType nativeClass,
[DynamicallyAccessedMembers (MethodsAndPrivateNested)]
43 changes: 0 additions & 43 deletions src/Mono.Android/Android.Runtime/JNIEnv.cs
Original file line number Diff line number Diff line change
@@ -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)
4 changes: 2 additions & 2 deletions src/Mono.Android/Android.Runtime/JNIEnvInit.cs
Original file line number Diff line number Diff line change
@@ -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;

3 changes: 0 additions & 3 deletions src/Mono.Android/Java.Interop/IJavaObjectEx.cs
Original file line number Diff line number Diff line change
@@ -3,9 +3,6 @@
namespace Java.Interop {

interface IJavaObjectEx {
IntPtr KeyHandle {get; set;}
bool IsProxy {get; set;}
bool NeedsActivation {get; set;}
IntPtr ToLocalJniHandle ();
}
}
2 changes: 1 addition & 1 deletion src/Mono.Android/Java.Interop/Runtime.cs
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ public static class Runtime {
[Obsolete ("Please use Java.Interop.JniEnvironment.Runtime.ValueManager.GetSurfacedPeers()")]
public static List<WeakReference> GetSurfacedObjects ()
{
var peers = JNIEnvInit.AndroidValueManager!.GetSurfacedPeers ();
var peers = JNIEnvInit.ValueManager!.GetSurfacedPeers ();
var r = new List<WeakReference> (peers.Count);
foreach (var p in peers) {
if (p.SurfacedPeer.TryGetTarget (out var target))
13 changes: 6 additions & 7 deletions src/Mono.Android/Java.Interop/TypeManager.cs
Original file line number Diff line number Diff line change
@@ -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);
}
182 changes: 29 additions & 153 deletions src/Mono.Android/Java.Lang/Object.cs
Original file line number Diff line number Diff line change
@@ -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 ()))
210 changes: 32 additions & 178 deletions src/Mono.Android/Java.Lang/Throwable.cs
Original file line number Diff line number Diff line change
@@ -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,43 +44,28 @@ 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)
{
if (handle == IntPtr.Zero)
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<Java.Lang.Throwable> (
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,31 +140,25 @@ 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<Java.Lang.Class> (value, JniHandleOwnership.TransferLocalRef);
}
}

[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)
{
}
}
8 changes: 4 additions & 4 deletions src/Mono.Android/PublicAPI/API-35/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
@@ -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
8 changes: 4 additions & 4 deletions src/Mono.Android/PublicAPI/API-36/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
@@ -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
2 changes: 0 additions & 2 deletions src/native/monodroid/osbridge.cc
Original file line number Diff line number Diff line change
@@ -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[] = {

0 comments on commit 4926f0c

Please sign in to comment.