Skip to content

Commit 811a58c

Browse files
grendellojonpryor
authored andcommitted
[Mono.Android] PeekObject() should consider target type (#717)
Fixes: https://bugzilla.xamarin.com/show_bug.cgi?id=58405 When using `Android.OS.Bundle` to retrieve a collection for a specific key one can either use the untyped `Get` method or one of the generic `Get*ArrayList` methods to retrieve the list. In the first case we will get back an `IList` instance, in the second case we'll get an `IList<T>` instance. However, if one calls `Get` first and then one of the generic methods next, for the same key, the result will be a thrown `InvalidCastException` because we attempt to cast the `IList` obtained from `Get` to `IList<T>` needed by the generic `Get*ArrayList` method. This happens because the same native handle is used in both cases and the first call to `Get` causes `Java.Lang.Object` to cache the `IList` instance so that the second call to `Get*ArrayList` gets the cached object instead of a new, properly typed, one. The solution is to extend `Java.Lang.Object.PeekObject` to allow specifying the desired type of the object corresponding to the native handle and, if the requirement isn't met, evict the entry from the cache returning `null` to the caller, thus letting it do the right thing by creating an instance of a class with correct type.
1 parent f87635b commit 811a58c

File tree

4 files changed

+57
-3
lines changed

4 files changed

+57
-3
lines changed

src/Mono.Android/Android.Runtime/JavaList.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ public static IList<T> FromJniHandle (IntPtr handle, JniHandleOwnership transfer
550550
if (handle == IntPtr.Zero)
551551
return null;
552552

553-
IJavaObject inst = Java.Lang.Object.PeekObject (handle);
553+
IJavaObject inst = Java.Lang.Object.PeekObject (handle, typeof (IList<T>));
554554
if (inst == null)
555555
inst = new JavaList<T> (handle, transfer);
556556
else

src/Mono.Android/Java.Lang/Object.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ internal static List<WeakReference> GetSurfacedObjects_ForDiagnosticsOnly ()
390390
}
391391
}
392392

393-
internal static IJavaObject PeekObject (IntPtr handle)
393+
internal static IJavaObject PeekObject (IntPtr handle, Type requiredType = null)
394394
{
395395
if (handle == IntPtr.Zero)
396396
return null;
@@ -401,14 +401,22 @@ internal static IJavaObject PeekObject (IntPtr handle)
401401
for (int i = 0; i < wrefs.Count; ++i) {
402402
var wref = wrefs [i];
403403
IJavaObject res = wref.Target as IJavaObject;
404-
if (res != null && res.Handle != IntPtr.Zero && JNIEnv.IsSameObject (handle, res.Handle))
404+
if (res != null && res.Handle != IntPtr.Zero && JNIEnv.IsSameObject (handle, res.Handle)) {
405+
if (requiredType != null && !requiredType.IsAssignableFrom (res.GetType ()))
406+
return null;
405407
return res;
408+
}
406409
}
407410
}
408411
}
409412
return null;
410413
}
411414

415+
internal static T PeekObject <T> (IntPtr handle)
416+
{
417+
return (T)PeekObject (handle, typeof (T));
418+
}
419+
412420
public static T GetObject<T> (IntPtr jnienv, IntPtr handle, JniHandleOwnership transfer)
413421
where T : class, IJavaObject
414422
{
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading;
4+
using Android.OS;
5+
6+
using NUnit.Framework;
7+
8+
namespace Xamarin.Android.RuntimeTests {
9+
10+
[TestFixture]
11+
public class BundleTest
12+
{
13+
[Test]
14+
public void TestBundleIntegerArrayList()
15+
{
16+
var b = new Bundle();
17+
b.PutIntegerArrayList("key", new List<Java.Lang.Integer>() { Java.Lang.Integer.ValueOf(1) });
18+
var list = b.GetIntegerArrayList ("key");
19+
Assert.NotNull (list, "'key' doesn't refer to a list of integers");
20+
var obj = b.Get ("key");
21+
Assert.NotNull (obj, "Missing 'key' in bundle");
22+
try {
23+
list = b.GetIntegerArrayList ("key");
24+
Assert.NotNull (list, "'key' doesn't refer to a list of integers after non-generic call");
25+
} catch (Exception e) {
26+
Assert.Fail ("Java.Lang.Object caches too aggresively");
27+
}
28+
}
29+
30+
[Test]
31+
public void TestBundleIntegerArrayList2 ()
32+
{
33+
var b = new Bundle();
34+
b.PutIntegerArrayList ("key", new List<Java.Lang.Integer> () { Java.Lang.Integer.ValueOf (1) });
35+
var obj = b.Get ("key");
36+
Assert.NotNull (obj, "Missing 'key' in bundle");
37+
try {
38+
var list = b.GetIntegerArrayList ("key");
39+
Assert.NotNull (list, "'key' doesn't refer to a list of integers");
40+
} catch (Exception e) {
41+
Assert.Fail ("Java.Lang.Object caches too aggresively");
42+
}
43+
}
44+
}
45+
}

src/Mono.Android/Test/Mono.Android-Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
<Compile Include="Xamarin.Android.RuntimeTests\MyIntent.cs" />
8585
<Compile Include="Xamarin.Android.RuntimeTests\NonJavaObject.cs" />
8686
<Compile Include="Xamarin.Android.RuntimeTests\TestInstrumentation.cs" />
87+
<Compile Include="Android.OS\BundleTest.cs" />
8788
</ItemGroup>
8889
<ItemGroup>
8990
<None Include="Resources\AboutResources.txt" />

0 commit comments

Comments
 (0)