Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Mono.Android] Java.Interop Unification! #9640

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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)]
Expand Down
43 changes: 0 additions & 43 deletions src/Mono.Android/Android.Runtime/JNIEnv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions src/Mono.Android/Android.Runtime/JNIEnvInit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

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

interface IJavaObjectEx {
IntPtr KeyHandle {get; set;}
bool IsProxy {get; set;}
bool NeedsActivation {get; set;}
IntPtr ToLocalJniHandle ();
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Mono.Android/Java.Interop/Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
13 changes: 6 additions & 7 deletions src/Mono.Android/Java.Interop/TypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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}'");
}
Expand Down Expand Up @@ -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);
}
Expand Down
Loading
Loading