Skip to content
Open
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
1 change: 1 addition & 0 deletions src/Mono.Android/Android.Runtime/AndroidEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ public static void RaiseThrowable (Java.Lang.Throwable throwable)
if (throwable == null)
throw new ArgumentNullException ("throwable");
JNIEnv.Throw (throwable.Handle);
GC.KeepAlive (throwable);
}

internal static void UnhandledException (Exception e)
Expand Down
8 changes: 5 additions & 3 deletions src/Mono.Android/Android.Runtime/JNIEnv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -506,9 +506,9 @@ public static IntPtr ToLocalJniHandle (IJavaObject? value)
if (value == null)
return IntPtr.Zero;
var ex = value as IJavaObjectEx;
if (ex != null)
return ex.ToLocalJniHandle ();
return NewLocalRef (value.Handle);
IntPtr result = ex != null ? ex.ToLocalJniHandle () : NewLocalRef (value.Handle);
GC.KeepAlive (value);
return result;
}

public static string? GetCharSequence (IntPtr jobject, JniHandleOwnership transfer)
Expand Down Expand Up @@ -895,6 +895,7 @@ public static void CopyArray (IJavaObject[] src, IntPtr dest)
for (int i = 0; i < src.Length; i++) {
IJavaObject o = src [i];
JniEnvironment.Arrays.SetObjectArrayElement (new JniObjectReference (dest), i, new JniObjectReference (o == null ? IntPtr.Zero : o.Handle));
GC.KeepAlive (o);
}
}

Expand Down Expand Up @@ -1545,6 +1546,7 @@ static IntPtr NewArray (Array value, Type elementType, IntPtr elementClass)
} },
{ typeof (IJavaObject), (dest, index, value) => {
SetObjectArrayElement (dest, index, value == null ? IntPtr.Zero : ((IJavaObject) value).Handle);
GC.KeepAlive (value);
} },
{ typeof (Array), (dest, index, value) => {
IntPtr _v = NewArray ((Array) value!);
Expand Down
25 changes: 1 addition & 24 deletions src/Mono.Android/Android.Runtime/JavaObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,7 @@ public static IntPtr GetHandle (object obj)
if (obj == null)
return IntPtr.Zero;

Type type = obj.GetType ();
if (type == typeof (bool))
return new Java.Lang.Boolean ((bool)obj).Handle;
else if (type == typeof (sbyte))
return new Java.Lang.Byte ((sbyte)obj).Handle;
else if (type == typeof (char))
return new Java.Lang.Character ((char)obj).Handle;
else if (type == typeof (short))
return new Java.Lang.Short ((short)obj).Handle;
else if (type == typeof (int))
return new Java.Lang.Integer ((int)obj).Handle;
else if (type == typeof (long))
return new Java.Lang.Long ((long)obj).Handle;
else if (type == typeof (float))
return new Java.Lang.Float ((float)obj).Handle;
else if (type == typeof (double))
return new Java.Lang.Double ((double)obj).Handle;
else if (type == typeof (string))
return JNIEnv.NewString ((string)obj);
else if (typeof (IJavaObject).IsAssignableFrom (type))
return ((IJavaObject)obj).Handle;
else
return new JavaObject (obj).Handle;
return Java.Interop.JavaConvert.ToLocalJniHandle (obj);
}

public static object? GetObject (IntPtr handle, JniHandleOwnership transfer)
Expand Down Expand Up @@ -112,4 +90,3 @@ public override string ToString ()
}
}
}

14 changes: 10 additions & 4 deletions src/Mono.Android/Android.Runtime/XmlReaderPullParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ public class XmlReaderResourceParser : XmlReaderPullParser, IXmlResourceParser
return IntPtr.Zero;

var xpr = value as XmlResourceParserReader;
if (xpr != null)
return JNIEnv.NewLocalRef (xpr.Handle);
if (xpr != null) {
IntPtr result = JNIEnv.NewLocalRef (xpr.Handle);
GC.KeepAlive (xpr);
return result;
}
return JNIEnv.ToLocalJniHandle (new Android.Runtime.XmlReaderResourceParser (value));
}

Expand Down Expand Up @@ -130,8 +133,11 @@ public static IntPtr ToLocalJniHandle (XmlReader? value)
return IntPtr.Zero;

var xppr = value as XmlPullParserReader;
if (xppr != null)
return JNIEnv.NewLocalRef (xppr.Handle);
if (xppr != null) {
IntPtr result = JNIEnv.NewLocalRef (xppr.Handle);
GC.KeepAlive (xppr);
return result;
}
return JNIEnv.ToLocalJniHandle (new Android.Runtime.XmlReaderPullParser (value));
}

Expand Down
2 changes: 2 additions & 0 deletions src/Mono.Android/Android.Widget/AbsListView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ public virtual void SetAdapter (Android.Widget.IListAdapter adapter)
JNIEnv.CallVoidMethod (Handle, id_setAdapter_Landroid_widget_ListAdapter_, new JValue (adapter));
else
JNIEnv.CallNonvirtualVoidMethod (Handle, ThresholdClass, id_setAdapter_Landroid_widget_ListAdapter_, new JValue (adapter));
GC.KeepAlive (adapter);
GC.KeepAlive (this);
}
#endif
}
Expand Down
6 changes: 5 additions & 1 deletion src/Mono.Android/Android.Widget/AdapterView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public AdapterView (Android.Content.Context context)
JniHandleOwnership.TransferLocalRef);
JNIEnv.FinishCreateInstance (Handle, "(Landroid/content/Context;)V", new JValue (context));
}
GC.KeepAlive (context);
}

static IntPtr id_ctor_Landroid_content_Context_Landroid_util_AttributeSet_;
Expand All @@ -104,6 +105,8 @@ public AdapterView (Android.Content.Context context, Android.Util.IAttributeSet
JniHandleOwnership.TransferLocalRef);
JNIEnv.FinishCreateInstance (Handle, "(Landroid/content/Context;Landroid/util/AttributeSet;)V", new JValue (context), new JValue (attrs));
}
GC.KeepAlive (context);
GC.KeepAlive (attrs);
}

static IntPtr id_ctor_Landroid_content_Context_Landroid_util_AttributeSet_I;
Expand All @@ -127,6 +130,8 @@ public AdapterView (Android.Content.Context context, Android.Util.IAttributeSet
JniHandleOwnership.TransferLocalRef);
JNIEnv.FinishCreateInstance (Handle, "(Landroid/content/Context;Landroid/util/AttributeSet;I)V", new JValue (context), new JValue (attrs), new JValue (defStyle));
}
GC.KeepAlive (context);
GC.KeepAlive (attrs);
}

protected override Java.Lang.Object? RawAdapter {
Expand All @@ -140,4 +145,3 @@ public abstract T Adapter {
}
}
}

9 changes: 7 additions & 2 deletions src/Mono.Android/Android.Widget/AdapterViewAnimator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,18 @@ public Android.Widget.IAdapter? Adapter {
get {
if (id_getAdapter == IntPtr.Zero)
id_getAdapter = JNIEnv.GetMethodID (class_ref, "getAdapter", "()Landroid/widget/Adapter;");
Android.Widget.IAdapter? result;
if (GetType () == ThresholdType)
return Java.Lang.Object.GetObject<Android.Widget.IAdapter> (JNIEnv.CallObjectMethod (Handle, id_getAdapter), JniHandleOwnership.TransferLocalRef);
result = Java.Lang.Object.GetObject<Android.Widget.IAdapter> (JNIEnv.CallObjectMethod (Handle, id_getAdapter), JniHandleOwnership.TransferLocalRef);
else
return Java.Lang.Object.GetObject<Android.Widget.IAdapter> (
result = Java.Lang.Object.GetObject<Android.Widget.IAdapter> (
JNIEnv.CallNonvirtualObjectMethod (
Handle,
ThresholdClass,
JNIEnv.GetMethodID (ThresholdClass, "getAdapter", "()Landroid/widget/Adapter;")),
JniHandleOwnership.TransferLocalRef);
GC.KeepAlive (this);
return result;
}
set {
if (id_setAdapter_Landroid_widget_Adapter_ == IntPtr.Zero)
Expand All @@ -108,6 +111,8 @@ public Android.Widget.IAdapter? Adapter {
ThresholdClass,
JNIEnv.GetMethodID (ThresholdClass, "setAdapter", "(Landroid/widget/Adapter;)V"),
new JValue (JNIEnv.ToJniHandle ((IJavaObject?) value)));
GC.KeepAlive (value);
GC.KeepAlive (this);
}

}
Expand Down
14 changes: 12 additions & 2 deletions src/Mono.Android/Android.Widget/ArrayAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public ArrayAdapter (Android.Content.Context context, int textViewResourceId)
JniHandleOwnership.TransferLocalRef);
JNIEnv.FinishCreateInstance (Handle, "(Landroid/content/Context;I)V", new JValue (context), new JValue (textViewResourceId));
}
GC.KeepAlive (context);
}

static IntPtr id_ctor_Landroid_content_Context_II;
Expand All @@ -62,6 +63,7 @@ public ArrayAdapter (Android.Content.Context context, int resource, int textView
JniHandleOwnership.TransferLocalRef);
JNIEnv.FinishCreateInstance (Handle, "(Landroid/content/Context;II)V", new JValue (context), new JValue (resource), new JValue (textViewResourceId));
}
GC.KeepAlive (context);
}

static IntPtr id_ctor_Landroid_content_Context_IarrayLjava_lang_Object_;
Expand All @@ -87,6 +89,7 @@ public ArrayAdapter (Android.Content.Context context, int textViewResourceId, T[
JNIEnv.FinishCreateInstance (Handle, "(Landroid/content/Context;I[Ljava/lang/Object;)V", new JValue (context), new JValue (textViewResourceId), new JValue (native_objects));
}
JNIEnv.DeleteLocalRef (native_objects);
GC.KeepAlive (context);
}

static IntPtr id_ctor_Landroid_content_Context_IIarrayLjava_lang_Object_;
Expand All @@ -112,6 +115,7 @@ public ArrayAdapter (Android.Content.Context context, int resource, int textView
JNIEnv.FinishCreateInstance (Handle, "(Landroid/content/Context;II[Ljava/lang/Object;)V", new JValue (context), new JValue (resource), new JValue (textViewResourceId), new JValue (native_objects));
}
JNIEnv.DeleteLocalRef (native_objects);
GC.KeepAlive (context);
}

static IntPtr id_ctor_Landroid_content_Context_ILjava_util_List_;
Expand All @@ -137,6 +141,7 @@ public ArrayAdapter (Android.Content.Context context, int textViewResourceId, Sy
JNIEnv.FinishCreateInstance (Handle, "(Landroid/content/Context;ILjava/util/List;)V", new JValue (context), new JValue (textViewResourceId), new JValue (lrefObjects));
}
JNIEnv.DeleteLocalRef (lrefObjects);
GC.KeepAlive (context);
}

static IntPtr id_ctor_Landroid_content_Context_IILjava_util_List_;
Expand All @@ -162,6 +167,7 @@ public ArrayAdapter (Android.Content.Context context, int resource, int textView
JNIEnv.FinishCreateInstance (Handle, "(Landroid/content/Context;IILjava/util/List;)V", new JValue (context), new JValue (resource), new JValue (textViewResourceId), new JValue (lrefObjects));
}
JNIEnv.DeleteLocalRef (lrefObjects);
GC.KeepAlive (context);
}

static IntPtr id_add_Ljava_lang_Object_;
Expand All @@ -182,9 +188,11 @@ public void Add (T @object)
{
if (id_createFromResource_Landroid_content_Context_II == IntPtr.Zero)
id_createFromResource_Landroid_content_Context_II = JNIEnv.GetStaticMethodID (class_ref, "createFromResource", "(Landroid/content/Context;II)Landroid/widget/ArrayAdapter;");
return JavaConvert.FromJniHandle<ArrayAdapter<Java.Lang.ICharSequence>> (
var result = JavaConvert.FromJniHandle<ArrayAdapter<Java.Lang.ICharSequence>> (
JNIEnv.CallStaticObjectMethod (class_ref, id_createFromResource_Landroid_content_Context_II, new JValue (context), new JValue (textArrayResId), new JValue (textViewResId)),
JniHandleOwnership.TransferLocalRef)!;
JniHandleOwnership.TransferLocalRef);
GC.KeepAlive (context);
return result ?? throw new InvalidOperationException ("Unable to marshal the return value to an Android.Widget.ArrayAdapter instance.");
}

static IntPtr id_getItem_I;
Expand Down Expand Up @@ -239,6 +247,8 @@ public void Remove (T @object)
if (id_sort_Ljava_util_Comparator_ == IntPtr.Zero)
id_sort_Ljava_util_Comparator_ = JNIEnv.GetMethodID (class_ref, "sort", "(Ljava/util/Comparator;)V");
JNIEnv.CallNonvirtualVoidMethod (Handle, class_ref, id_sort_Ljava_util_Comparator_, new JValue (comparator));
GC.KeepAlive (comparator);
GC.KeepAlive (this);
}
}
}
3 changes: 2 additions & 1 deletion src/Mono.Android/Android.Widget/TextView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ void AddTextChangedListener (TextWatcherImplementor watcher)
if (id_addTextChangedListener == IntPtr.Zero)
id_addTextChangedListener = JNIEnv.GetMethodID (class_ref, "addTextChangedListener", "(Landroid/text/TextWatcher;)V");
JNIEnv.CallVoidMethod (Handle, id_addTextChangedListener, new JValue (watcher));
GC.KeepAlive (watcher);
GC.KeepAlive (this);
}

WeakReference? implementor_TextWatcher;
Expand Down Expand Up @@ -69,4 +71,3 @@ public event EventHandler<TextChangedEventArgs> TextChanged {
}
}
}

8 changes: 6 additions & 2 deletions src/Mono.Android/Java.Interop/JavaObjectExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ internal static TResult? _JavaCast<
if (instance.Handle == IntPtr.Zero)
throw new ObjectDisposedException (instance.GetType ().FullName);

return (TResult?) Java.Lang.Object.GetObject (instance.Handle, JniHandleOwnership.DoNotTransfer, typeof (TResult)) ??
var result = (TResult?) Java.Lang.Object.GetObject (instance.Handle, JniHandleOwnership.DoNotTransfer, typeof (TResult));
GC.KeepAlive (instance);
return result ??
throw new InvalidCastException (
FormattableString.Invariant ($"Unable to convert instance of type '{instance.GetType ().FullName}' to type '{typeof (TResult).FullName}'."));
}
Expand All @@ -100,7 +102,9 @@ internal static TResult? _JavaCast<
if (resultType.IsAssignableFrom (instance.GetType ()))
return instance;

return (IJavaObject?) Java.Lang.Object.GetObject (instance.Handle, JniHandleOwnership.DoNotTransfer, resultType) ??
var result = (IJavaObject?) Java.Lang.Object.GetObject (instance.Handle, JniHandleOwnership.DoNotTransfer, resultType);
GC.KeepAlive (instance);
return result ??
throw new InvalidCastException (
FormattableString.Invariant ($"Unable to convert instance of type '{instance.GetType ().FullName}' to type '{resultType.FullName}'."));
}
Expand Down
4 changes: 3 additions & 1 deletion src/Mono.Android/Java.Interop/Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ public static bool IsGCUserPeer (IJavaObject value)
{
if (value == null)
return false;
return IsGCUserPeer (value.Handle);
bool result = IsGCUserPeer (value.Handle);
GC.KeepAlive (value);
return result;
}

public static bool IsGCUserPeer (IntPtr value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1746,6 +1746,7 @@ KeyStore GetConfiguredKeyStoreInstance ()
var keyFactory = KeyFactory.GetInstance (algorithmName) ?? throw new InvalidOperationException ($"Failed to get the KeyFactory instance for algorithm {algorithmName}");
var privateKey = keyFactory.GeneratePrivate (new Java.Security.Spec.PKCS8EncodedKeySpec (key.ExportPkcs8PrivateKey ()));
var certificate = Java.Lang.Object.GetObject<Certificate> (clientCertificate.Handle, JniHandleOwnership.DoNotTransfer);
GC.KeepAlive (clientCertificate);

if (privateKey is null || certificate is null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,42 @@ public void ManagedToJavaTypeMapping ()
Assert.AreEqual (null, m, "`JnienvTest` does *not* subclass Java.Lang.Object, it should *not* be in the typemap!");
}

[Test]
[Category ("GCBridge")]
[Category ("NativeAOTIgnore")]
public void ToLocalJniHandleSurvivesConcurrentCollection ()
{
const int iterations = 1000;
using var done = new CancellationTokenSource ();
var collector = new Thread (() => {
while (!done.IsCancellationRequested) {
GC.Collect ();
GC.WaitForPendingFinalizers ();
Thread.Yield ();
}
});
collector.Start ();

try {
for (int i = 0; i < iterations; i++) {
IntPtr handle = JNIEnv.ToLocalJniHandle (new FinalizableHandleOwner ());
try {
Assert.AreNotEqual (IntPtr.Zero, handle, $"No local reference was returned during iteration {i}.");
Assert.IsNotEmpty (JNIEnv.GetClassNameFromInstance (handle), $"The local reference was invalid during iteration {i}.");
} finally {
JNIEnv.DeleteLocalRef (handle);
}
}
} finally {
done.Cancel ();
collector.Join ();
}
}

sealed class FinalizableHandleOwner : Java.Lang.Object
{
}

[Test, Category ("GCBridge")]
[Ignore ("Failing in NativeAOT: https://github.com/dotnet/android/issues/11690")]
public void DoNotLeakWeakReferences ()
Expand Down