diff --git a/Xamarin.Android-Tests.sln b/Xamarin.Android-Tests.sln
index 0d6de66b5dc..ee7d94b987e 100644
--- a/Xamarin.Android-Tests.sln
+++ b/Xamarin.Android-Tests.sln
@@ -90,6 +90,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Android-Test.Library",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Android-TestsMultiDex", "tests\Runtime-MultiDex\Mono.Android-TestsMultiDex.csproj", "{9ECBEA14-B79F-4F92-9266-495C03A32571}"
EndProject
+Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Interop-Tests", "external\Java.Interop\src\Java.Interop\Tests\Interop-Tests.shproj", "{0ADB8D72-7479-49AF-8809-E03AE4A4EAE2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Linq.Expressions", "external\Java.Interop\lib\mono.linq.expressions\Mono.Linq.Expressions.csproj", "{0C001D50-4176-45AE-BDC8-BA626508B0CC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop.GenericMarshaler", "external\Java.Interop\src\Java.Interop.GenericMarshaler\Java.Interop.GenericMarshaler.csproj", "{D1243BAB-23CA-4566-A2A3-3ADA2C2DC3AF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop-Tests", "src\Mono.Android\Test\Java.Interop-Tests\Java.Interop-Tests.csproj", "{6CB00820-A66B-43E5-8785-ED456C6E9F39}"
+EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Mono.Android\Test\Mono.Android-Test.Shared.projitems*{0ab4956e-6fb9-4da0-9d49-ab65a3ff403a}*SharedItemsImports = 13
@@ -249,6 +257,18 @@ Global
{9ECBEA14-B79F-4F92-9266-495C03A32571}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9ECBEA14-B79F-4F92-9266-495C03A32571}.Release|Any CPU.Build.0 = Release|Any CPU
{9ECBEA14-B79F-4F92-9266-495C03A32571}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {0C001D50-4176-45AE-BDC8-BA626508B0CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0C001D50-4176-45AE-BDC8-BA626508B0CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0C001D50-4176-45AE-BDC8-BA626508B0CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0C001D50-4176-45AE-BDC8-BA626508B0CC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D1243BAB-23CA-4566-A2A3-3ADA2C2DC3AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D1243BAB-23CA-4566-A2A3-3ADA2C2DC3AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D1243BAB-23CA-4566-A2A3-3ADA2C2DC3AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D1243BAB-23CA-4566-A2A3-3ADA2C2DC3AF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6CB00820-A66B-43E5-8785-ED456C6E9F39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6CB00820-A66B-43E5-8785-ED456C6E9F39}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6CB00820-A66B-43E5-8785-ED456C6E9F39}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6CB00820-A66B-43E5-8785-ED456C6E9F39}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -284,6 +304,10 @@ Global
{0AB4956E-6FB9-4DA0-9D49-AB65A3FF403A} = {EFBC4DC0-DBFF-4DAA-B0B8-6D0CB02A25F5}
{8CB5FF58-FF95-43B9-9064-9ACE9525866F} = {EFBC4DC0-DBFF-4DAA-B0B8-6D0CB02A25F5}
{9ECBEA14-B79F-4F92-9266-495C03A32571} = {EFBC4DC0-DBFF-4DAA-B0B8-6D0CB02A25F5}
+ {0ADB8D72-7479-49AF-8809-E03AE4A4EAE2} = {EFBC4DC0-DBFF-4DAA-B0B8-6D0CB02A25F5}
+ {0C001D50-4176-45AE-BDC8-BA626508B0CC} = {EFBC4DC0-DBFF-4DAA-B0B8-6D0CB02A25F5}
+ {D1243BAB-23CA-4566-A2A3-3ADA2C2DC3AF} = {EFBC4DC0-DBFF-4DAA-B0B8-6D0CB02A25F5}
+ {6CB00820-A66B-43E5-8785-ED456C6E9F39} = {EFBC4DC0-DBFF-4DAA-B0B8-6D0CB02A25F5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8643CD20-B195-4919-8135-27549488237E}
diff --git a/build-tools/scripts/Jar.targets b/build-tools/scripts/Jar.targets
index 327013349b9..c58761be90f 100644
--- a/build-tools/scripts/Jar.targets
+++ b/build-tools/scripts/Jar.targets
@@ -25,10 +25,12 @@
<_Jar>"$(JarPath)"
<_Targets>-source $(_JavacSourceVersion) -target $(_JavacTargetVersion)
<_DestDir>$(IntermediateOutputPath)__CreateTestJarFile-bin
- <_AndroidJar>-cp "$(AndroidSdkDirectory)\platforms\android-$(_AndroidApiLevelName)\android.jar"
+ <_AndroidJar>-bootclasspath "$(AndroidSdkDirectory)\platforms\android-$(_AndroidApiLevelName)\android.jar"
+ <_JIJar>$([System.IO.Path]::GetFullPath ('$(XAInstallPrefix)'))\xbuild\Xamarin\Android\java-interop.jar
+ <_CP>-cp "$(_JIJar)"
-
+
(value.Handle, JniHandleOwnership.DoNotTransfer);
- JniObjectReference.Dispose (ref value, transfer);
- var p = throwable as JavaProxyThrowable;
- if (p != null)
- return p.InnerException;
- return throwable;
+ if (!reference.IsValid)
+ return null;
+ var peeked = JNIEnv.AndroidValueManager.PeekPeer (reference);
+ var peekedExc = peeked as Exception;
+ if (peekedExc == null) {
+ var throwable = Java.Lang.Object.GetObject (reference.Handle, JniHandleOwnership.DoNotTransfer);
+ JniObjectReference.Dispose (ref reference, options);
+ return throwable;
+ }
+ JniObjectReference.Dispose (ref reference, options);
+ var unwrapped = JNIEnv.AndroidValueManager.UnboxException (peeked);
+ if (unwrapped != null) {
+ return unwrapped;
+ }
+ return peekedExc;
}
public override void RaisePendingException (Exception pendingException)
@@ -222,11 +231,12 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl
protected override IEnumerable GetSimpleReferences (Type type)
{
- var j = JNIEnv.GetJniName (type);
- if (j == null)
+ var j = JNIEnv.monodroid_typemap_managed_to_java (type.FullName + ", " + type.Assembly.GetName ().Name);
+ if (j == IntPtr.Zero)
return base.GetSimpleReferences (type);
+ var s = Marshal.PtrToStringAnsi (j);
return base.GetSimpleReferences (type)
- .Concat (Enumerable.Repeat (j, 1));
+ .Concat (new [] { s });
}
delegate Delegate GetCallbackHandler ();
@@ -318,17 +328,21 @@ static bool CallRegisterMethodByIndex (JniNativeMethodRegistrationArguments argu
}
}
- public override void RegisterNativeMembers (JniType jniType, Type type, string methods)
+ public override void RegisterNativeMembers (JniType nativeClass, Type type, string methods)
{
- if (FastRegisterNativeMembers (jniType, type, methods))
+ if (FastRegisterNativeMembers (nativeClass, type, methods))
return;
- if (methods == null)
+ if (string.IsNullOrEmpty (methods)) {
+ base.RegisterNativeMembers (nativeClass, type, methods);
return;
+ }
string[] members = methods.Split ('\n');
- if (members.Length == 0)
+ if (members.Length < 2) {
+ base.RegisterNativeMembers (nativeClass, type, methods);
return;
+ }
JniNativeMethodRegistration[] natives = new JniNativeMethodRegistration [members.Length-1];
for (int i = 0; i < members.Length; ++i) {
@@ -351,41 +365,344 @@ public override void RegisterNativeMembers (JniType jniType, Type type, string m
natives [i] = new JniNativeMethodRegistration (toks [0], toks [1], callback);
}
- JniEnvironment.Types.RegisterNatives (jniType.PeerReference, natives, natives.Length);
+ JniEnvironment.Types.RegisterNatives (nativeClass.PeerReference, natives, natives.Length);
}
}
class AndroidValueManager : JniRuntime.JniValueManager {
+ Dictionary instances = new Dictionary ();
+
public override void WaitForGCBridgeProcessing ()
{
JNIEnv.WaitForBridgeProcessing ();
}
+ public override IJavaPeerable CreatePeer (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType)
+ {
+ if (!reference.IsValid)
+ return null;
+
+ var peer = Java.Interop.TypeManager.CreateInstance (reference.Handle, JniHandleOwnership.DoNotTransfer, targetType) as IJavaPeerable;
+ JniObjectReference.Dispose (ref reference, options);
+ return peer;
+ }
+
public override void AddPeer (IJavaPeerable value)
{
+ if (value == null)
+ throw new ArgumentNullException (nameof (value));
+ if (!value.PeerReference.IsValid)
+ throw new ArgumentException ("Must have a valid JNI object reference!", nameof (value));
+
+ var reference = value.PeerReference;
+ var hash = JNIEnv.IdentityHash (reference.Handle);
+
+ AddPeer (value, reference, hash);
+ }
+
+ internal void AddPeer (IJavaPeerable value, JniObjectReference reference, IntPtr hash)
+ {
+ lock (instances) {
+ IdentityHashTargets targets;
+ if (!instances.TryGetValue (hash, out targets)) {
+ targets = new IdentityHashTargets (value);
+ instances.Add (hash, targets);
+ return;
+ }
+ bool found = false;
+ for (int i = 0; i < targets.Count; ++i) {
+ IJavaPeerable target;
+ var wref = targets [i];
+ if (ShouldReplaceMapping (wref, reference, out target)) {
+ found = true;
+ targets [i] = IdentityHashTargets.CreateWeakReference (value);
+ break;
+ }
+ if (JniEnvironment.Types.IsSameObject (value.PeerReference, target.PeerReference)) {
+ found = true;
+ if (Logger.LogGlobalRef) {
+ Logger.Log (LogLevel.Info, "monodroid-gref",
+ string.Format ("warning: not replacing previous registered handle {0} with handle {1} for key_handle 0x{2}",
+ target.PeerReference.ToString (), reference.ToString (), hash.ToString ("x")));
+ }
+ }
+ }
+ if (!found) {
+ targets.Add (value);
+ }
+ }
+ }
+
+ internal void AddPeer (IJavaPeerable value, IntPtr handle, JniHandleOwnership transfer, out IntPtr handleField)
+ {
+ if (handle == IntPtr.Zero) {
+ handleField = handle;
+ return;
+ }
+
+ var transferType = transfer & (JniHandleOwnership.DoNotTransfer | JniHandleOwnership.TransferLocalRef | JniHandleOwnership.TransferGlobalRef);
+ switch (transferType) {
+ case JniHandleOwnership.DoNotTransfer:
+ handleField = JNIEnv.NewGlobalRef (handle);
+ break;
+ case JniHandleOwnership.TransferLocalRef:
+ handleField = JNIEnv.NewGlobalRef (handle);
+ JNIEnv.DeleteLocalRef (handle);
+ break;
+ case JniHandleOwnership.TransferGlobalRef:
+ handleField = handle;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException ("transfer", transfer,
+ "Invalid `transfer` value: " + transfer + " on type " + value.GetType ());
+ }
+ if (handleField == IntPtr.Zero)
+ throw new InvalidOperationException ("Unable to allocate Global Reference for object '" + value.ToString () + "'!");
+
+ IntPtr hash = JNIEnv.IdentityHash (handleField);
+ value.SetJniIdentityHashCode ((int) hash);
+ if ((transfer & JniHandleOwnership.DoNotRegister) == 0) {
+ AddPeer (value, new JniObjectReference (handleField, JniObjectReferenceType.Global), hash);
+ }
+
+ if (Logger.LogGlobalRef) {
+ JNIEnv._monodroid_gref_log ("handle 0x" + handleField.ToString ("x") +
+ "; key_handle 0x" + hash.ToString ("x") +
+ ": Java Type: `" + JNIEnv.GetClassNameFromInstance (handleField) + "`; " +
+ "MCW type: `" + value.GetType ().FullName + "`\n");
+ }
+ }
+
+ bool ShouldReplaceMapping (WeakReference current, JniObjectReference reference, out IJavaPeerable target)
+ {
+ target = null;
+
+ if (current == null)
+ return true;
+
+ // Target has been GC'd; see also FIXME, above, in finalizer
+ if (!current.TryGetTarget (out target) || target == null)
+ return true;
+
+ // It's possible that the instance was GC'd, but the finalizer
+ // hasn't executed yet, so the `instances` entry is stale.
+ if (!target.PeerReference.IsValid)
+ return true;
+
+ if (!JniEnvironment.Types.IsSameObject (target.PeerReference, reference))
+ return false;
+
+ // JNIEnv.NewObject/JNIEnv.CreateInstance() compatibility.
+ // When two MCW's are created for one Java instance [0],
+ // we want the 2nd MCW to replace the 1st, as the 2nd is
+ // the one the dev created; the 1st is an implicit intermediary.
+ //
+ // [0]: If Java ctor invokes overridden virtual method, we'll
+ // transition into managed code w/o a registered instance, and
+ // thus will create an "intermediary" via
+ // (IntPtr, JniHandleOwnership) .ctor.
+ if ((target.JniManagedPeerState & JniManagedPeerStates.Replaceable) == JniManagedPeerStates.Replaceable)
+ return true;
+
+ return false;
}
public override void RemovePeer (IJavaPeerable value)
{
+ if (value == null)
+ throw new ArgumentNullException (nameof (value));
+ if (!value.PeerReference.IsValid)
+ throw new ArgumentException ("Must have a valid JNI object reference!", nameof (value));
+
+ var reference = value.PeerReference;
+ var hash = JNIEnv.IdentityHash (reference.Handle);
+
+ RemovePeer (value, hash);
+ }
+
+ internal void RemovePeer (IJavaPeerable value, IntPtr hash)
+ {
+ lock (instances) {
+ IdentityHashTargets targets;
+ if (!instances.TryGetValue (hash, out targets)) {
+ return;
+ }
+ for (int i = targets.Count - 1; i >= 0; i--) {
+ var wref = targets [i];
+ if (!wref.TryGetTarget (out IJavaPeerable target)) {
+ // wref is invalidated; remove it.
+ targets.RemoveAt (i);
+ continue;
+ }
+ if (!object.ReferenceEquals (target, value)) {
+ continue;
+ }
+ targets.RemoveAt (i);
+ }
+ if (targets.Count == 0) {
+ instances.Remove (hash);
+ }
+ }
}
public override IJavaPeerable PeekPeer (JniObjectReference reference)
{
- return (IJavaPeerable) Java.Lang.Object.GetObject (reference.Handle, JniHandleOwnership.DoNotTransfer);
+ if (!reference.IsValid)
+ return null;
+
+ var hash = JNIEnv.IdentityHash (reference.Handle);
+ lock (instances) {
+ IdentityHashTargets targets;
+ if (instances.TryGetValue (hash, out targets)) {
+ for (int i = targets.Count - 1; i >= 0; i--) {
+ var wref = targets [i];
+ if (!wref.TryGetTarget (out var result) || !result.PeerReference.IsValid) {
+ targets.RemoveAt (i);
+ continue;
+ }
+ if (!JniEnvironment.Types.IsSameObject (reference, result.PeerReference))
+ continue;
+ return result;
+ }
+ }
+ }
+ return null;
+ }
+
+ protected override bool TryUnboxPeerObject (IJavaPeerable value, out object result)
+ {
+ var proxy = value as JavaProxyThrowable;
+ if (proxy != null) {
+ result = proxy.InnerException;
+ return true;
+ }
+ return base.TryUnboxPeerObject (value, out result);
+ }
+
+ internal Exception UnboxException (IJavaPeerable value)
+ {
+ object r;
+ if (TryUnboxPeerObject (value, out r) && r is Exception e) {
+ return e;
+ }
+ return null;
}
public override void CollectPeers ()
{
+ GC.Collect ();
}
public override void FinalizePeer (IJavaPeerable value)
{
+ if (value == null)
+ throw new ArgumentNullException (nameof (value));
+
+ if (Logger.LogGlobalRef) {
+ JNIEnv._monodroid_gref_log ($"Finalizing handle {value.PeerReference}\n");
+ }
+
+ // 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.
+ if (value.PeerReference.IsValid) {
+ GC.ReRegisterForFinalize (value);
+ } else {
+ RemovePeer (value, (IntPtr) value.JniIdentityHashCode);
+ value.SetPeerReference (new JniObjectReference ());
+ value.Finalized ();
+ }
}
public override List GetSurfacedPeers ()
{
- return null;
+ lock (instances) {
+ var surfacedPeers = new List (instances.Count);
+ foreach (var e in instances) {
+ for (int i = 0; i < e.Value.Count; i++) {
+ var value = e.Value [i];
+ surfacedPeers.Add (new JniSurfacedPeerInfo (e.Key.ToInt32 (), value));
+ }
+ }
+ return surfacedPeers;
+ }
+ }
+ }
+
+ class InstancesKeyComparer : IEqualityComparer
+ {
+
+ public bool Equals (IntPtr x, IntPtr y)
+ {
+ return x == y;
+ }
+
+ public int GetHashCode (IntPtr value)
+ {
+ return value.GetHashCode ();
+ }
+ }
+
+ class IdentityHashTargets {
+ WeakReference first;
+ List> rest;
+
+ public static WeakReference CreateWeakReference (IJavaPeerable value)
+ {
+ return new WeakReference (value, trackResurrection: true);
+ }
+
+ public IdentityHashTargets (IJavaPeerable value)
+ {
+ first = CreateWeakReference (value);
+ }
+
+ public int Count => (first != null ? 1 : 0) + (rest == null ? rest.Count : 0);
+
+ public WeakReference this [int index] {
+ get {
+ if (index == 0)
+ return first;
+ index -= 1;
+ if (rest == null || index >= rest.Count)
+ return null;
+ return rest [index];
+ }
+ set {
+ if (index == 0) {
+ first = value;
+ return;
+ }
+ index -= 1;
+ rest [index] = value;
+ }
+ }
+
+ public void Add (IJavaPeerable value)
+ {
+ if (first == null) {
+ first = CreateWeakReference (value);
+ return;
+ }
+ if (rest == null)
+ rest = new List> ();
+ rest.Add (CreateWeakReference (value));
+ }
+
+ public void RemoveAt (int index)
+ {
+ if (index == 0) {
+ first = null;
+ if (rest?.Count > 0) {
+ first = rest [0];
+ rest.RemoveAt (0);
+ }
+ return;
+ }
+ index -= 1;
+ rest.RemoveAt (index);
}
}
}
diff --git a/src/Mono.Android/Android.Runtime/InputStreamInvoker.cs b/src/Mono.Android/Android.Runtime/InputStreamInvoker.cs
index 40241fab78a..813eed6f9cf 100644
--- a/src/Mono.Android/Android.Runtime/InputStreamInvoker.cs
+++ b/src/Mono.Android/Android.Runtime/InputStreamInvoker.cs
@@ -65,10 +65,10 @@ public static Stream FromJniHandle (IntPtr handle, JniHandleOwnership transfer)
if (handle == IntPtr.Zero)
return null;
- IJavaObject inst = Java.Lang.Object.PeekObject (handle);
+ IJavaObject inst = (IJavaObject) Java.Lang.Object.PeekObject (handle);
if (inst == null)
- inst = Java.Interop.TypeManager.CreateInstance (handle, transfer);
+ inst = (IJavaObject) Java.Interop.TypeManager.CreateInstance (handle, transfer);
else
JNIEnv.DeleteRef (handle, transfer);
diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs
index b17086da4c9..0603a1480f2 100644
--- a/src/Mono.Android/Android.Runtime/JNIEnv.cs
+++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs
@@ -57,6 +57,8 @@ public static partial class JNIEnv {
static AndroidRuntime androidRuntime;
+ internal static AndroidValueManager AndroidValueManager;
+
#if !JAVA_INTEROP
static JNIInvokeInterface invoke_iface;
@@ -195,6 +197,7 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args)
#if JAVA_INTEROP
androidRuntime = new AndroidRuntime (args->env, args->javaVm, androidSdkVersion > 10, args->grefLoader, args->Loader_loadClass);
+ AndroidValueManager = (AndroidValueManager) androidRuntime.ValueManager;
#endif // JAVA_INTEROP
if (Logger.LogTiming) {
diff --git a/src/Mono.Android/Android.Runtime/JavaCollection.cs b/src/Mono.Android/Android.Runtime/JavaCollection.cs
index a1dd231ef91..0f7eb6e04bc 100644
--- a/src/Mono.Android/Android.Runtime/JavaCollection.cs
+++ b/src/Mono.Android/Android.Runtime/JavaCollection.cs
@@ -118,7 +118,7 @@ public static ICollection FromJniHandle (IntPtr handle, JniHandleOwnership trans
if (handle == IntPtr.Zero)
return null;
- IJavaObject inst = Java.Lang.Object.PeekObject (handle);
+ IJavaObject inst = (IJavaObject) Java.Lang.Object.PeekObject (handle);
if (inst == null)
inst = new JavaCollection (handle, transfer);
else
@@ -243,7 +243,7 @@ public static ICollection FromJniHandle (IntPtr handle, JniHandleOwnership tr
if (handle == IntPtr.Zero)
return null;
- IJavaObject inst = Java.Lang.Object.PeekObject (handle);
+ IJavaObject inst = (IJavaObject) Java.Lang.Object.PeekObject (handle);
if (inst == null)
inst = new JavaCollection (handle, transfer);
else
diff --git a/src/Mono.Android/Android.Runtime/JavaDictionary.cs b/src/Mono.Android/Android.Runtime/JavaDictionary.cs
index 4d433d86eb0..dd5e6ae9603 100644
--- a/src/Mono.Android/Android.Runtime/JavaDictionary.cs
+++ b/src/Mono.Android/Android.Runtime/JavaDictionary.cs
@@ -226,7 +226,7 @@ public static IDictionary FromJniHandle (IntPtr handle, JniHandleOwnership trans
if (handle == IntPtr.Zero)
return null;
- IJavaObject inst = Java.Lang.Object.PeekObject (handle);
+ IJavaObject inst = (IJavaObject) Java.Lang.Object.PeekObject (handle);
if (inst == null)
inst = new JavaDictionary (handle, transfer);
else
@@ -404,7 +404,7 @@ public static IDictionary FromJniHandle (IntPtr handle, JniHandleOwnership
if (handle == IntPtr.Zero)
return null;
- IJavaObject inst = Java.Lang.Object.PeekObject (handle);
+ IJavaObject inst = (IJavaObject) Java.Lang.Object.PeekObject (handle);
if (inst == null)
inst = new JavaDictionary (handle, transfer);
else
diff --git a/src/Mono.Android/Android.Runtime/JavaList.cs b/src/Mono.Android/Android.Runtime/JavaList.cs
index 9eae03fdc64..9cc35810f87 100644
--- a/src/Mono.Android/Android.Runtime/JavaList.cs
+++ b/src/Mono.Android/Android.Runtime/JavaList.cs
@@ -229,7 +229,7 @@ public static IList FromJniHandle (IntPtr handle, JniHandleOwnership transfer)
if (handle == IntPtr.Zero)
return null;
- IJavaObject inst = Java.Lang.Object.PeekObject (handle);
+ IJavaObject inst = (IJavaObject) Java.Lang.Object.PeekObject (handle);
if (inst == null)
inst = new JavaList (handle, transfer);
else
@@ -550,7 +550,7 @@ public static IList FromJniHandle (IntPtr handle, JniHandleOwnership transfer
if (handle == IntPtr.Zero)
return null;
- IJavaObject inst = Java.Lang.Object.PeekObject (handle, typeof (IList));
+ IJavaObject inst = (IJavaObject) Java.Lang.Object.PeekObject (handle, typeof (IList));
if (inst == null)
inst = new JavaList (handle, transfer);
else
diff --git a/src/Mono.Android/Android.Runtime/JavaSet.cs b/src/Mono.Android/Android.Runtime/JavaSet.cs
index b4fa8fb9628..4c728337af8 100644
--- a/src/Mono.Android/Android.Runtime/JavaSet.cs
+++ b/src/Mono.Android/Android.Runtime/JavaSet.cs
@@ -146,7 +146,7 @@ public static ICollection FromJniHandle (IntPtr handle, JniHandleOwnership trans
if (handle == IntPtr.Zero)
return null;
- IJavaObject inst = Java.Lang.Object.PeekObject (handle);
+ IJavaObject inst = (IJavaObject) Java.Lang.Object.PeekObject (handle);
if (inst == null)
inst = new JavaSet (handle, transfer);
else
@@ -261,7 +261,7 @@ public static ICollection FromJniHandle (IntPtr handle, JniHandleOwnership tr
if (handle == IntPtr.Zero)
return null;
- IJavaObject inst = Java.Lang.Object.PeekObject (handle);
+ IJavaObject inst = (IJavaObject) Java.Lang.Object.PeekObject (handle);
if (inst == null)
inst = new JavaSet (handle, transfer);
else
diff --git a/src/Mono.Android/Android.Runtime/OutputStreamInvoker.cs b/src/Mono.Android/Android.Runtime/OutputStreamInvoker.cs
index 11d9415724b..5f7ea672a81 100644
--- a/src/Mono.Android/Android.Runtime/OutputStreamInvoker.cs
+++ b/src/Mono.Android/Android.Runtime/OutputStreamInvoker.cs
@@ -72,10 +72,10 @@ internal static Stream FromNative (IntPtr handle, JniHandleOwnership transfer)
if (handle == IntPtr.Zero)
return null;
- IJavaObject inst = Java.Lang.Object.PeekObject (handle);
+ IJavaObject inst = (IJavaObject) Java.Lang.Object.PeekObject (handle);
if (inst == null)
- inst = Java.Interop.TypeManager.CreateInstance (handle, transfer);
+ inst = (IJavaObject) Java.Interop.TypeManager.CreateInstance (handle, transfer);
else
JNIEnv.DeleteRef (handle, transfer);
diff --git a/src/Mono.Android/Android.Runtime/XmlPullParserReader.cs b/src/Mono.Android/Android.Runtime/XmlPullParserReader.cs
index 1bc5102633a..32a5f525bad 100644
--- a/src/Mono.Android/Android.Runtime/XmlPullParserReader.cs
+++ b/src/Mono.Android/Android.Runtime/XmlPullParserReader.cs
@@ -35,9 +35,9 @@ static XmlResourceParserReader FromNative (IntPtr handle, JniHandleOwnership tra
{
if (handle == IntPtr.Zero)
return null;
- IJavaObject inst = Java.Lang.Object.PeekObject (handle);
+ IJavaObject inst = (IJavaObject) Java.Lang.Object.PeekObject (handle);
if (inst == null)
- inst = Java.Interop.TypeManager.CreateInstance (handle, transfer);
+ inst = (IJavaObject) Java.Interop.TypeManager.CreateInstance (handle, transfer);
else
JNIEnv.DeleteRef (handle, transfer);
return new XmlResourceParserReader (inst.JavaCast ());
@@ -392,9 +392,9 @@ static XmlReader FromNative (IntPtr handle, JniHandleOwnership transfer)
{
if (handle == IntPtr.Zero)
return null;
- IJavaObject inst = Java.Lang.Object.PeekObject (handle);
+ IJavaObject inst = (IJavaObject) Java.Lang.Object.PeekObject (handle);
if (inst == null)
- inst = Java.Interop.TypeManager.CreateInstance (handle, transfer);
+ inst = (IJavaObject) Java.Interop.TypeManager.CreateInstance (handle, transfer);
else
JNIEnv.DeleteRef (handle, transfer);
return new XmlPullParserReader (inst.JavaCast ());
diff --git a/src/Mono.Android/Java.Interop/JavaConvert.cs b/src/Mono.Android/Java.Interop/JavaConvert.cs
index fb1a3c76e01..399a59a18ec 100644
--- a/src/Mono.Android/Java.Interop/JavaConvert.cs
+++ b/src/Mono.Android/Java.Interop/JavaConvert.cs
@@ -104,7 +104,7 @@ public static T FromJniHandle(IntPtr handle, JniHandleOwnership transfer, out
return default (T);
}
- IJavaObject interned = Java.Lang.Object.PeekObject (handle);
+ IJavaObject interned = (IJavaObject) Java.Lang.Object.PeekObject (handle);
if (interned != null) {
T r = FromJavaObject(interned, out set);
if (set) {
@@ -138,7 +138,7 @@ public static object FromJniHandle (IntPtr handle, JniHandleOwnership transfer,
return null;
}
- IJavaObject interned = Java.Lang.Object.PeekObject (handle);
+ IJavaObject interned = (IJavaObject) Java.Lang.Object.PeekObject (handle);
if (interned != null) {
var unwrapped = FromJavaObject (interned, targetType);
if (unwrapped != null) {
diff --git a/src/Mono.Android/Java.Interop/JavaObjectExtensions.cs b/src/Mono.Android/Java.Interop/JavaObjectExtensions.cs
index 2a67b624367..90516e0ba0a 100644
--- a/src/Mono.Android/Java.Interop/JavaObjectExtensions.cs
+++ b/src/Mono.Android/Java.Interop/JavaObjectExtensions.cs
@@ -109,7 +109,7 @@ internal static IJavaObject JavaCast (IJavaObject instance, Type resultType)
return CastClass (instance, resultType);
}
else if (resultType.IsInterface) {
- return Java.Lang.Object.GetObject (instance.Handle, JniHandleOwnership.DoNotTransfer, resultType);
+ return (IJavaObject) Java.Lang.Object.GetObject (instance.Handle, JniHandleOwnership.DoNotTransfer, resultType);
}
else
throw new NotSupportedException (string.Format ("Unable to convert type '{0}' to '{1}'.",
diff --git a/src/Mono.Android/Java.Interop/Runtime.cs b/src/Mono.Android/Java.Interop/Runtime.cs
index 7cfa945db63..1c5b5bb96fe 100644
--- a/src/Mono.Android/Java.Interop/Runtime.cs
+++ b/src/Mono.Android/Java.Interop/Runtime.cs
@@ -10,9 +10,16 @@ public static class Runtime {
internal static IntPtr grefIGCUserPeer_class;
+ [Obsolete ("Please use Java.Interop.JniEnvironment.Runtime.ValueManager.GetSurfacedPeers()")]
public static List GetSurfacedObjects ()
{
- return Java.Lang.Object.GetSurfacedObjects_ForDiagnosticsOnly ();
+ var peers = JNIEnv.AndroidValueManager.GetSurfacedPeers ();
+ var r = new List (peers.Count);
+ foreach (var p in peers) {
+ if (p.SurfacedPeer.TryGetTarget (out var target))
+ r.Add (new WeakReference (target, trackResurrection: true));
+ }
+ return r;
}
[DllImport ("__Internal")]
diff --git a/src/Mono.Android/Java.Interop/TypeManager.cs b/src/Mono.Android/Java.Interop/TypeManager.cs
index 49d4108eb13..e75cbe540d0 100644
--- a/src/Mono.Android/Java.Interop/TypeManager.cs
+++ b/src/Mono.Android/Java.Interop/TypeManager.cs
@@ -231,12 +231,12 @@ internal static Type GetJavaToManagedType (string class_name)
return null;
}
- internal static IJavaObject CreateInstance (IntPtr handle, JniHandleOwnership transfer)
+ internal static IJavaPeerable CreateInstance (IntPtr handle, JniHandleOwnership transfer)
{
return CreateInstance (handle, transfer, null);
}
- internal static IJavaObject CreateInstance (IntPtr handle, JniHandleOwnership transfer, Type targetType)
+ internal static IJavaPeerable CreateInstance (IntPtr handle, JniHandleOwnership transfer, Type targetType)
{
Type type = null;
IntPtr class_ptr = JNIEnv.GetObjectClass (handle);
@@ -279,13 +279,13 @@ internal static IJavaObject CreateInstance (IntPtr handle, JniHandleOwnership tr
}
- IJavaObject result = null;
+ IJavaPeerable result = null;
try {
- result = (IJavaObject) CreateProxy (type, handle, transfer);
- var ex = result as IJavaObjectEx;
- if (Runtime.IsGCUserPeer (result) && ex != null)
- ex.IsProxy = true;
+ result = (IJavaPeerable) CreateProxy (type, handle, transfer);
+ if (Runtime.IsGCUserPeer (result.PeerReference.Handle)) {
+ result.SetJniManagedPeerState (JniManagedPeerStates.Replaceable);
+ }
} catch (MissingMethodException e) {
var key_handle = JNIEnv.IdentityHash (handle);
JNIEnv.DeleteRef (handle, transfer);
@@ -297,18 +297,30 @@ internal static IJavaObject CreateInstance (IntPtr handle, JniHandleOwnership tr
return result;
}
+ static readonly Type[] XAConstructorSignature = new Type [] { typeof (IntPtr), typeof (JniHandleOwnership) };
+ static readonly Type[] JIConstructorSignature = new Type [] { typeof (JniObjectReference).MakeByRefType (), typeof (JniObjectReferenceOptions) };
+
internal static object CreateProxy (Type type, IntPtr handle, JniHandleOwnership transfer)
{
// Skip Activator.CreateInstance() as that requires public constructors,
// and we want to hide some constructors for sanity reasons.
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
- ConstructorInfo c = type.GetConstructor (flags, null, new[]{typeof (IntPtr), typeof (JniHandleOwnership)}, null);
- if (c == null) {
- throw new MissingMethodException (
- "No constructor found for " + type.FullName + "::.ctor(System.IntPtr, Android.Runtime.JniHandleOwnership)",
- CreateJavaLocationException ());
+ ConstructorInfo c = type.GetConstructor (flags, null, XAConstructorSignature, null);
+ if (c != null) {
+ return c.Invoke (new object [] { handle, transfer });
+ }
+ c = type.GetConstructor (flags, null, JIConstructorSignature, null);
+ if (c != null) {
+ JniObjectReference r = new JniObjectReference (handle);
+ JniObjectReferenceOptions o = JniObjectReferenceOptions.Copy;
+ var peer = (IJavaPeerable) c.Invoke (new object [] { r, o });
+ JNIEnv.DeleteRef (handle, transfer);
+ peer.SetJniManagedPeerState (peer.JniManagedPeerState | JniManagedPeerStates.Replaceable);
+ return peer;
}
- return c.Invoke (new object[]{handle, transfer});
+ throw new MissingMethodException (
+ "No constructor found for " + type.FullName + "::.ctor(System.IntPtr, Android.Runtime.JniHandleOwnership)",
+ CreateJavaLocationException ());
}
public static void RegisterType (string java_class, Type t)
diff --git a/src/Mono.Android/Java.Lang/Object.cs b/src/Mono.Android/Java.Lang/Object.cs
index 496a235b1ce..142b6cf1383 100644
--- a/src/Mono.Android/Java.Lang/Object.cs
+++ b/src/Mono.Android/Java.Lang/Object.cs
@@ -15,9 +15,6 @@ public partial class Object : IDisposable, IJavaObject, IJavaObjectEx
, IJavaPeerable
#endif // JAVA_INTEROP
{
-
- static Dictionary> instances = new Dictionary> (new InstancesKeyComparer ());
-
IntPtr key_handle;
IntPtr weak_handle;
JObjectRefType handle_type;
@@ -52,21 +49,12 @@ IntPtr IJavaObjectEx.ToLocalJniHandle ()
~Object ()
{
- if (Logger.LogGlobalRef) {
- JNIEnv._monodroid_gref_log (
- string.Format ("Finalizing handle 0x{0}\n", handle.ToString ("x")));
- }
// 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 (handle != IntPtr.Zero)
- GC.ReRegisterForFinalize (this);
- else {
- Dispose (false);
- DeregisterInstance (this, key_handle);
- }
+ JniEnvironment.Runtime.ValueManager.FinalizePeer (this);
}
public Object (IntPtr handle, JniHandleOwnership transfer)
@@ -163,17 +151,17 @@ void IJavaPeerable.DisposeUnlessReferenced ()
public void UnregisterFromRuntime ()
{
- DeregisterInstance (this, key_handle);
+ JNIEnv.AndroidValueManager.RemovePeer (this, key_handle);
}
void IJavaPeerable.Disposed ()
{
- throw new NotSupportedException ();
+ Dispose (disposing: true);
}
void IJavaPeerable.Finalized ()
{
- throw new NotSupportedException ();
+ Dispose (disposing: false);
}
void IJavaPeerable.SetJniIdentityHashCode (int value)
@@ -200,16 +188,14 @@ void IJavaPeerable.SetPeerReference (JniObjectReference reference)
public void Dispose ()
{
- Dispose (true);
- Dispose (this, ref handle, key_handle, handle_type);
- GC.SuppressFinalize (this);
+ JNIEnv.AndroidValueManager.DisposePeer (this);
}
protected virtual void Dispose (bool disposing)
{
}
- internal static void Dispose (object instance, ref IntPtr handle, IntPtr key_handle, JObjectRefType handle_type)
+ internal static void Dispose (IJavaPeerable instance, ref IntPtr handle, IntPtr key_handle, JObjectRefType handle_type)
{
if (handle == IntPtr.Zero)
return;
@@ -219,7 +205,7 @@ internal static void Dispose (object instance, ref IntPtr handle, IntPtr key_han
string.Format ("Disposing handle 0x{0}\n", handle.ToString ("x")));
}
- DeregisterInstance (instance, key_handle);
+ JNIEnv.AndroidValueManager.RemovePeer (instance, key_handle);
switch (handle_type) {
case JObjectRefType.Global:
@@ -242,174 +228,18 @@ internal static void Dispose (object instance, ref IntPtr handle, IntPtr key_han
protected void SetHandle (IntPtr value, JniHandleOwnership transfer)
{
- RegisterInstance (this, value, transfer, out handle);
+ JNIEnv.AndroidValueManager.AddPeer (this, value, transfer, out handle);
handle_type = JObjectRefType.Global;
}
- internal static void RegisterInstance (IJavaObject instance, IntPtr value, JniHandleOwnership transfer, out IntPtr handle)
- {
- if (value == IntPtr.Zero) {
- handle = value;
- return;
- }
-
- var transferType = transfer & (JniHandleOwnership.DoNotTransfer | JniHandleOwnership.TransferLocalRef | JniHandleOwnership.TransferGlobalRef);
- switch (transferType) {
- case JniHandleOwnership.DoNotTransfer:
- handle = JNIEnv.NewGlobalRef (value);
- break;
- case JniHandleOwnership.TransferLocalRef:
- handle = JNIEnv.NewGlobalRef (value);
- JNIEnv.DeleteLocalRef (value);
- break;
- case JniHandleOwnership.TransferGlobalRef:
- handle = value;
- break;
- default:
- throw new ArgumentOutOfRangeException ("transfer", transfer,
- "Invalid `transfer` value: " + transfer + " on type " + instance.GetType ());
- }
- if (handle == IntPtr.Zero)
- throw new InvalidOperationException ("Unable to allocate Global Reference for object '" + instance.ToString () + "'!");
-
- IntPtr key = JNIEnv.IdentityHash (handle);
- if ((transfer & JniHandleOwnership.DoNotRegister) == 0) {
- _RegisterInstance (instance, key, handle);
- }
- var ex = instance as IJavaObjectEx;
- if (ex != null)
- ex.KeyHandle = key;
-
- if (Logger.LogGlobalRef) {
- JNIEnv._monodroid_gref_log ("handle 0x" + handle.ToString ("x") +
- "; key_handle 0x" + key.ToString ("x") +
- ": Java Type: `" + JNIEnv.GetClassNameFromInstance (handle) + "`; " +
- "MCW type: `" + instance.GetType ().FullName + "`\n");
- }
- }
-
- static void _RegisterInstance (IJavaObject instance, IntPtr key, IntPtr handle)
- {
- lock (instances) {
- List wrefs;
- if (!instances.TryGetValue (key, out wrefs)) {
- wrefs = new List (1) {
- new WeakReference (instance, true),
- };
- instances.Add (key, wrefs);
- }
- else {
- bool found = false;
- for (int i = 0; i < wrefs.Count; ++i) {
- var wref = wrefs [i];
- if (ShouldReplaceMapping (wref, handle)) {
- found = true;
- wrefs.Remove (wref);
- wrefs.Add (new WeakReference (instance, true));
- break;
- }
- var cur = wref == null ? null : (IJavaObject) wref.Target;
- var _c = cur == null ? IntPtr.Zero : cur.Handle;
- if (_c != IntPtr.Zero && JNIEnv.IsSameObject (handle, _c)) {
- found = true;
- if (Logger.LogGlobalRef) {
- Logger.Log (LogLevel.Info, "monodroid-gref",
- string.Format ("warning: not replacing previous registered handle 0x{0} with handle 0x{1} for key_handle 0x{2}",
- _c.ToString ("x"), handle.ToString ("x"), key.ToString ("x")));
- }
- break;
- }
- }
- if (!found) {
- wrefs.Add (new WeakReference (instance, true));
- }
- }
- }
- }
-
- static bool ShouldReplaceMapping (WeakReference current, IntPtr handle)
+ internal static IJavaPeerable PeekObject (IntPtr handle, Type requiredType = null)
{
- if (current == null)
- return true;
-
- // Target has been GC'd; see also FIXME, above, in finalizer
- object target = current.Target;
- if (target == null)
- return true;
-
- // It's possible that the instance was GC'd, but the finalizer
- // hasn't executed yet, so the `instances` entry is stale.
- var ijo = (IJavaObject) target;
- if (ijo.Handle == IntPtr.Zero)
- return true;
-
- if (!JNIEnv.IsSameObject (ijo.Handle, handle))
- return false;
-
- // JNIEnv.NewObject/JNIEnv.CreateInstance() compatibility.
- // When two MCW's are created for one Java instance [0],
- // we want the 2nd MCW to replace the 1st, as the 2nd is
- // the one the dev created; the 1st is an implicit intermediary.
- //
- // [0]: If Java ctor invokes overridden virtual method, we'll
- // transition into managed code w/o a registered instance, and
- // thus will create an "intermediary" via
- // (IntPtr, JniHandleOwnership) .ctor.
- var ex = target as IJavaObjectEx;
- if (ex != null && ex.IsProxy)
- return true;
-
- return false;
- }
-
- internal static void DeregisterInstance (object instance, IntPtr key_handle)
- {
- lock (instances) {
- List wrefs;
- if (instances.TryGetValue (key_handle, out wrefs)) {
- for (int i = wrefs.Count-1; i >= 0; --i) {
- var wref = wrefs [i];
- if (wref.Target == null || wref.Target == instance) {
- wrefs.RemoveAt (i);
- }
- }
- if (wrefs.Count == 0)
- instances.Remove (key_handle);
- }
- }
- }
-
- internal static List GetSurfacedObjects_ForDiagnosticsOnly ()
- {
- lock (instances) {
- var surfaced = new List (instances.Count);
- foreach (var e in instances) {
- surfaced.AddRange (e.Value);
- }
- return surfaced;
- }
- }
-
- internal static IJavaObject PeekObject (IntPtr handle, Type requiredType = null)
- {
- if (handle == IntPtr.Zero)
+ var peeked = JNIEnv.AndroidValueManager.PeekPeer (new JniObjectReference (handle));
+ if (peeked == null)
return null;
-
- lock (instances) {
- List wrefs;
- if (instances.TryGetValue (JNIEnv.IdentityHash (handle), out wrefs)) {
- for (int i = 0; i < wrefs.Count; ++i) {
- var wref = wrefs [i];
- IJavaObject res = wref.Target as IJavaObject;
- if (res != null && res.Handle != IntPtr.Zero && JNIEnv.IsSameObject (handle, res.Handle)) {
- if (requiredType != null && !requiredType.IsAssignableFrom (res.GetType ()))
- return null;
- return res;
- }
- }
- }
- }
- return null;
+ if (requiredType != null && !requiredType.IsAssignableFrom (peeked.GetType ()))
+ return null;
+ return peeked;
}
internal static T PeekObject (IntPtr handle)
@@ -438,30 +268,15 @@ internal static T _GetObject (IntPtr handle, JniHandleOwnership transfer)
return (T) GetObject (handle, transfer, typeof (T));
}
- internal static IJavaObject GetObject (IntPtr handle, JniHandleOwnership transfer, Type type = null)
+ internal static IJavaPeerable GetObject (IntPtr handle, JniHandleOwnership transfer, Type type = null)
{
if (handle == IntPtr.Zero)
return null;
- lock (instances) {
- List wrefs;
- if (instances.TryGetValue (JNIEnv.IdentityHash (handle), out wrefs)) {
- for (int i = 0; i < wrefs.Count; ++i) {
- var wref = wrefs [i];
- var result = wref.Target as IJavaObject;
- var exists = result != null && result.Handle != IntPtr.Zero && JNIEnv.IsSameObject (handle, result.Handle);
- if (exists) {
- if (type == null ? true : type.IsAssignableFrom (result.GetType ())) {
- JNIEnv.DeleteRef (handle, transfer);
- return result;
- }
- /*
- Logger.Log (LogLevel.Warn, "*jonp*", "# jonp: Object.GetObject: handle=0x" + handle.ToString ("x") + " found but is of type '" + result.GetType ().FullName +
- "' and not the required targetType of '" + type + "'.");
- */
- }
- }
- }
+ var r = PeekObject (handle, type);
+ if (r != null) {
+ JNIEnv.DeleteRef (handle, transfer);
+ return r;
}
return Java.Interop.TypeManager.CreateInstance (handle, transfer, type);
@@ -725,17 +540,4 @@ public static explicit operator string[] (Java.Lang.Object value)
return value.ToArray();
}
}
-
- class InstancesKeyComparer : IEqualityComparer {
-
- public bool Equals (IntPtr x, IntPtr y)
- {
- return x == y;
- }
-
- public int GetHashCode (IntPtr value)
- {
- return value.GetHashCode ();
- }
- }
}
diff --git a/src/Mono.Android/Java.Lang/Throwable.cs b/src/Mono.Android/Java.Lang/Throwable.cs
index d562565096d..8554c637764 100644
--- a/src/Mono.Android/Java.Lang/Throwable.cs
+++ b/src/Mono.Android/Java.Lang/Throwable.cs
@@ -226,7 +226,7 @@ public unsafe Java.Lang.Class Class {
protected void SetHandle (IntPtr value, JniHandleOwnership transfer)
{
- Java.Lang.Object.RegisterInstance (this, value, transfer, out handle);
+ JNIEnv.AndroidValueManager.AddPeer (this, value, transfer, out handle);
handle_type = JObjectRefType.Global;
}
@@ -251,18 +251,8 @@ public static System.Exception ToException (Throwable e)
~Throwable ()
{
- if (Logger.LogGlobalRef) {
- JNIEnv._monodroid_gref_log (
- string.Format ("Finalizing Throwable handle 0x{0}\n", handle.ToString ("x")));
- }
-
refs_added = 0;
- if (handle != IntPtr.Zero)
- GC.ReRegisterForFinalize (this);
- else {
- Dispose (false);
- Object.DeregisterInstance (this, key_handle);
- }
+ JniEnvironment.Runtime.ValueManager.FinalizePeer (this);
}
#if JAVA_INTEROP
@@ -288,17 +278,17 @@ void IJavaPeerable.DisposeUnlessReferenced ()
public void UnregisterFromRuntime ()
{
- Object.DeregisterInstance (this, key_handle);
+ JNIEnv.AndroidValueManager.RemovePeer (this, key_handle);
}
void IJavaPeerable.Disposed ()
{
- throw new NotSupportedException ();
+ Dispose (disposing: true);
}
void IJavaPeerable.Finalized ()
{
- throw new NotSupportedException ();
+ Dispose (disposing: false);
}
void IJavaPeerable.SetJniIdentityHashCode (int value)
@@ -324,10 +314,7 @@ void IJavaPeerable.SetPeerReference (JniObjectReference reference)
public void Dispose ()
{
- Dispose (true);
- Java.Lang.Object.Dispose (this, ref handle, key_handle, handle_type);
- key_handle = IntPtr.Zero;
- GC.SuppressFinalize (this);
+ JNIEnv.AndroidValueManager.DisposePeer (this);
}
protected virtual void Dispose (bool disposing)
diff --git a/src/Mono.Android/Test/Java.Interop-Tests/.gitignore b/src/Mono.Android/Test/Java.Interop-Tests/.gitignore
new file mode 100644
index 00000000000..d996c0b7286
--- /dev/null
+++ b/src/Mono.Android/Test/Java.Interop-Tests/.gitignore
@@ -0,0 +1 @@
+Jars
diff --git a/src/Mono.Android/Test/Java.Interop-Tests/Java.Interop-Tests.csproj b/src/Mono.Android/Test/Java.Interop-Tests/Java.Interop-Tests.csproj
new file mode 100644
index 00000000000..5009aa123f1
--- /dev/null
+++ b/src/Mono.Android/Test/Java.Interop-Tests/Java.Interop-Tests.csproj
@@ -0,0 +1,86 @@
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {6CB00820-A66B-43E5-8785-ED456C6E9F39}
+ {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ {9ef11e43-1701-4396-8835-8392d57abb70}
+ Library
+ Properties
+ Java.Interop_Tests
+ Java.Interop-Tests
+ 512
+ Resources\Resource.designer.cs
+ Off
+ v9.0
+ true
+
+
+
+ $(AndroidFrameworkVersion)
+ true
+ portable
+ false
+ bin\Debug\
+ DEBUG;TRACE;NO_MARSHAL_MEMBER_BUILDER_SUPPORT
+ prompt
+ 4
+
+
+ $(AndroidFrameworkVersion)
+
+ true
+ bin\Release\
+ TRACE;NO_MARSHAL_MEMBER_BUILDER_SUPPORT
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {D1243BAB-23CA-4566-A2A3-3ADA2C2DC3AF}
+ Java.Interop.GenericMarshaler
+
+
+ {0C001D50-4176-45AE-BDC8-BA626508B0CC}
+ Mono.Linq.Expressions
+
+
+
+
+
+
+
+ BuildTestJarFile;
+ _CopyTestJarFiles;
+ $(BuildDependsOn)
+
+
+
+
+ CleanTestJarFile;
+ $(CleanDependsOn);
+ CleanLocal;
+
+
+
\ No newline at end of file
diff --git a/src/Mono.Android/Test/Java.Interop-Tests/Java.Interop-Tests.targets b/src/Mono.Android/Test/Java.Interop-Tests/Java.Interop-Tests.targets
new file mode 100644
index 00000000000..e4804b6d206
--- /dev/null
+++ b/src/Mono.Android/Test/Java.Interop-Tests/Java.Interop-Tests.targets
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+ Jars\Mono.Android-Test-classes.jar
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Mono.Android/Test/Java.Interop-Tests/Java.InteropTests/JavaInterop_Tests_Reference.cs b/src/Mono.Android/Test/Java.Interop-Tests/Java.InteropTests/JavaInterop_Tests_Reference.cs
new file mode 100644
index 00000000000..e0a898e5cd8
--- /dev/null
+++ b/src/Mono.Android/Test/Java.Interop-Tests/Java.InteropTests/JavaInterop_Tests_Reference.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Java.InteropTests
+{
+ // Exists for "easy" reference by Mono.Android-Tests.dll
+ public class JavaInterop_Tests_Reference
+ {
+ }
+}
diff --git a/src/Mono.Android/Test/Java.Interop-Tests/Properties/AssemblyInfo.cs b/src/Mono.Android/Test/Java.Interop-Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000000..887802784b2
--- /dev/null
+++ b/src/Mono.Android/Test/Java.Interop-Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,30 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Android.App;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Java.Interop_Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Java.Interop_Tests")]
+[assembly: AssemblyCopyright("Copyright © 2018")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: ComVisible(false)]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/Mono.Android/Test/Java.Interop-Tests/Resources/AboutResources.txt b/src/Mono.Android/Test/Java.Interop-Tests/Resources/AboutResources.txt
new file mode 100644
index 00000000000..c2bca974c48
--- /dev/null
+++ b/src/Mono.Android/Test/Java.Interop-Tests/Resources/AboutResources.txt
@@ -0,0 +1,44 @@
+Images, layout descriptions, binary blobs and string dictionaries can be included
+in your application as resource files. Various Android APIs are designed to
+operate on the resource IDs instead of dealing with images, strings or binary blobs
+directly.
+
+For example, a sample Android app that contains a user interface layout (main.axml),
+an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
+would keep its resources in the "Resources" directory of the application:
+
+Resources/
+ drawable/
+ icon.png
+
+ layout/
+ main.axml
+
+ values/
+ strings.xml
+
+In order to get the build system to recognize Android resources, set the build action to
+"AndroidResource". The native Android APIs do not operate directly with filenames, but
+instead operate on resource IDs. When you compile an Android application that uses resources,
+the build system will package the resources for distribution and generate a class called "R"
+(this is an Android convention) that contains the tokens for each one of the resources
+included. For example, for the above Resources layout, this is what the R class would expose:
+
+public class R {
+ public class drawable {
+ public const int icon = 0x123;
+ }
+
+ public class layout {
+ public const int main = 0x456;
+ }
+
+ public class strings {
+ public const int first_string = 0xabc;
+ public const int second_string = 0xbcd;
+ }
+}
+
+You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main
+to reference the layout/main.axml file, or R.strings.first_string to reference the first
+string in the dictionary file values/strings.xml.
\ No newline at end of file
diff --git a/src/Mono.Android/Test/Mono.Android-Tests.csproj b/src/Mono.Android/Test/Mono.Android-Tests.csproj
index 4364a8d886c..88e9976dfc0 100644
--- a/src/Mono.Android/Test/Mono.Android-Tests.csproj
+++ b/src/Mono.Android/Test/Mono.Android-Tests.csproj
@@ -87,17 +87,23 @@
{CB2335CB-0050-4020-8A05-E9614EDAA05E}
TestRunner.NUnit
+
+ {6CB00820-A66B-43E5-8785-ED456C6E9F39}
+ Java.Interop-Tests
+
{8CB5FF58-FF95-43B9-9064-9ACE9525866F}
Mono.Android-Test.Library
-
+
+
+
Xamarin.Android.Build.Tasks
{3F1F2F50-AF1A-4A5A-BEDB-193372F068D7}
False
False
-
+
Xamarin.Android.NUnitLite
{4D603AA3-3BFD-43C8-8050-0CD6C2601126}
False
diff --git a/src/Mono.Android/Test/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs b/src/Mono.Android/Test/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs
index 2da4d177c7b..0a58cd6bc81 100644
--- a/src/Mono.Android/Test/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs
+++ b/src/Mono.Android/Test/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs
@@ -30,10 +30,13 @@ protected NUnitInstrumentation(IntPtr handle, JniHandleOwnership transfer)
protected override IList GetTestAssemblies()
{
Assembly asm = Assembly.GetExecutingAssembly();
+ Assembly ji = typeof (Java.InteropTests.JavaInterop_Tests_Reference).Assembly;
+
return new List()
{
- new TestAssemblyInfo(asm, asm.Location ?? String.Empty)
+ new TestAssemblyInfo (asm, asm.Location ?? String.Empty),
+ new TestAssemblyInfo (ji, ji.Location ?? String.Empty),
};
}
}
diff --git a/src/Mono.Android/Test/Xamarin.Android.RuntimeTests/TestInstrumentation.cs b/src/Mono.Android/Test/Xamarin.Android.RuntimeTests/TestInstrumentation.cs
index e3dbc895242..1dfae2ed6af 100644
--- a/src/Mono.Android/Test/Xamarin.Android.RuntimeTests/TestInstrumentation.cs
+++ b/src/Mono.Android/Test/Xamarin.Android.RuntimeTests/TestInstrumentation.cs
@@ -21,6 +21,7 @@ public TestInstrumentation (IntPtr handle, JniHandleOwnership transfer)
protected override void AddTests ()
{
AddTest (Assembly.GetExecutingAssembly ());
+ AddTest (typeof (Java.InteropTests.JavaInterop_Tests_Reference).Assembly);
}
}
}
diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc
index 284b0bab96f..d360f6aa215 100644
--- a/src/monodroid/jni/monodroid-glue.cc
+++ b/src/monodroid/jni/monodroid-glue.cc
@@ -1188,7 +1188,9 @@ init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass runtimeClass, jobj
assm = utils.monodroid_load_assembly (domain, "Mono.Android");
image = mono_assembly_get_image (assm);
- for (uint32_t i = 0; i < OSBridge::NUM_GC_BRIDGE_TYPES; ++i) {
+ uint32_t i = 0;
+
+ for ( ; i < OSBridge::NUM_XA_GC_BRIDGE_TYPES; ++i) {
lookup_bridge_info (domain, image, &osBridge.get_java_gc_bridge_type (i), &osBridge.get_java_gc_bridge_info (i));
}
@@ -1200,6 +1202,13 @@ init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass runtimeClass, jobj
log_fatal (LOG_DEFAULT, "INTERNAL ERROR: Unable to find Android.Runtime.JNIEnv.Initialize!");
exit (FATAL_EXIT_MISSING_INIT);
}
+
+ MonoAssembly *ji_assm = utils.monodroid_load_assembly (domain, "Java.Interop");
+ MonoImage *ji_image = mono_assembly_get_image (ji_assm);
+ for ( ; i < OSBridge::NUM_XA_GC_BRIDGE_TYPES + OSBridge::NUM_JI_GC_BRIDGE_TYPES; ++i) {
+ lookup_bridge_info (domain, ji_image, &osBridge.get_java_gc_bridge_type (i), &osBridge.get_java_gc_bridge_info (i));
+ }
+
/* If running on desktop, we may be swapping in a new Mono.Android image when calling this
* so always make sure we have the freshest handle to the method.
*/
diff --git a/src/monodroid/jni/osbridge.cc b/src/monodroid/jni/osbridge.cc
index bf6ca1e9534..96e5d0dc352 100644
--- a/src/monodroid/jni/osbridge.cc
+++ b/src/monodroid/jni/osbridge.cc
@@ -29,17 +29,25 @@ FILE *lref_log;
using namespace xamarin::android;
using namespace xamarin::android::internal;
-const OSBridge::MonoJavaGCBridgeType OSBridge::mono_java_gc_bridge_types[] = {
+const OSBridge::MonoJavaGCBridgeType OSBridge::mono_xa_gc_bridge_types[] = {
{ "Java.Lang", "Object" },
{ "Java.Lang", "Throwable" },
};
+const OSBridge::MonoJavaGCBridgeType OSBridge::mono_ji_gc_bridge_types[] = {
+ { "Java.Interop", "JavaObject" },
+ { "Java.Interop", "JavaException" },
+};
+
const OSBridge::MonoJavaGCBridgeType OSBridge::empty_bridge_type = {
"",
""
};
-const uint32_t OSBridge::NUM_GC_BRIDGE_TYPES = (sizeof (mono_java_gc_bridge_types)/sizeof (mono_java_gc_bridge_types [0]));
+const uint32_t OSBridge::NUM_XA_GC_BRIDGE_TYPES = (sizeof (mono_xa_gc_bridge_types)/sizeof (mono_xa_gc_bridge_types [0]));
+const uint32_t OSBridge::NUM_JI_GC_BRIDGE_TYPES = (sizeof (mono_ji_gc_bridge_types)/sizeof (mono_ji_gc_bridge_types [0]));
+const uint32_t OSBridge::NUM_GC_BRIDGE_TYPES = NUM_XA_GC_BRIDGE_TYPES + NUM_JI_GC_BRIDGE_TYPES;
+
OSBridge::MonoJavaGCBridgeInfo OSBridge::mono_java_gc_bridge_info [NUM_GC_BRIDGE_TYPES];
OSBridge::MonoJavaGCBridgeInfo OSBridge::empty_bridge_info = {
diff --git a/src/monodroid/jni/osbridge.hh b/src/monodroid/jni/osbridge.hh
index 0452314e2a7..3c2e99b5568 100644
--- a/src/monodroid/jni/osbridge.hh
+++ b/src/monodroid/jni/osbridge.hh
@@ -55,11 +55,14 @@ namespace xamarin::android::internal
using MonodroidGCTakeRefFunc = mono_bool (OSBridge::*) (JNIEnv *env, MonoObject *obj);
static const MonoJavaGCBridgeType empty_bridge_type;
- static const MonoJavaGCBridgeType mono_java_gc_bridge_types[];
+ static const MonoJavaGCBridgeType mono_xa_gc_bridge_types[];
+ static const MonoJavaGCBridgeType mono_ji_gc_bridge_types[];
static MonoJavaGCBridgeInfo empty_bridge_info;
static MonoJavaGCBridgeInfo mono_java_gc_bridge_info [];
public:
+ static const uint32_t NUM_XA_GC_BRIDGE_TYPES;
+ static const uint32_t NUM_JI_GC_BRIDGE_TYPES;
static const uint32_t NUM_GC_BRIDGE_TYPES;
public:
@@ -73,10 +76,15 @@ namespace xamarin::android::internal
const MonoJavaGCBridgeType& get_java_gc_bridge_type (uint32_t index)
{
- if (index >= NUM_GC_BRIDGE_TYPES)
- return empty_bridge_type; // Not ideal...
+ if (index < NUM_XA_GC_BRIDGE_TYPES)
+ return mono_xa_gc_bridge_types [index];
+
+ index -= NUM_XA_GC_BRIDGE_TYPES;
+ if (index < NUM_JI_GC_BRIDGE_TYPES)
+ return mono_ji_gc_bridge_types [index];
- return mono_java_gc_bridge_types [index];
+ index -= NUM_JI_GC_BRIDGE_TYPES;
+ return empty_bridge_type; // Not ideal...
}
MonoJavaGCBridgeInfo& get_java_gc_bridge_info (uint32_t index)
diff --git a/tests/Runtime-MultiDex/Mono.Android-TestsMultiDex.csproj b/tests/Runtime-MultiDex/Mono.Android-TestsMultiDex.csproj
index 43a7707417d..5494960b455 100644
--- a/tests/Runtime-MultiDex/Mono.Android-TestsMultiDex.csproj
+++ b/tests/Runtime-MultiDex/Mono.Android-TestsMultiDex.csproj
@@ -97,22 +97,28 @@
{CB2335CB-0050-4020-8A05-E9614EDAA05E}
TestRunner.NUnit
-
+
+ {6CB00820-A66B-43E5-8785-ED456C6E9F39}
+ Java.Interop-Tests
+
+
+ {8CB5FF58-FF95-43B9-9064-9ACE9525866F}
+ Mono.Android-Test.Library
+
+
+
+
Xamarin.Android.Build.Tasks
{3F1F2F50-AF1A-4A5A-BEDB-193372F068D7}
False
False
-
+
Xamarin.Android.NUnitLite
{4D603AA3-3BFD-43C8-8050-0CD6C2601126}
False
False
-
- {8CB5FF58-FF95-43B9-9064-9ACE9525866F}
- Mono.Android-Test.Library
-
diff --git a/tests/TestRunner.Core/TestRunner.Core.csproj b/tests/TestRunner.Core/TestRunner.Core.csproj
index b7627a66dae..6e0b2cdb5a5 100644
--- a/tests/TestRunner.Core/TestRunner.Core.csproj
+++ b/tests/TestRunner.Core/TestRunner.Core.csproj
@@ -8,15 +8,15 @@
Library
Xamarin.Android.UnitTests
TestRunner.Core
- v8.1
+ v9.0
Resources\Resource.designer.cs
Resource
Resources
Assets
- true
+ $(AndroidFrameworkVersion)
true
false
bin\Debug
@@ -26,6 +26,7 @@
None
+ $(AndroidFrameworkVersion)
true
true
bin\Release
diff --git a/tests/TestRunner.NUnit/TestRunner.NUnit.csproj b/tests/TestRunner.NUnit/TestRunner.NUnit.csproj
index 9a162b00004..e85d234a57d 100644
--- a/tests/TestRunner.NUnit/TestRunner.NUnit.csproj
+++ b/tests/TestRunner.NUnit/TestRunner.NUnit.csproj
@@ -8,15 +8,15 @@
Library
Xamarin.Android.UnitTests.NUnit
TestRunner.NUnit
- v8.1
+ v9.0
Resources\Resource.designer.cs
Resource
Resources
Assets
- true
+ $(AndroidFrameworkVersion)
true
false
bin\Debug
@@ -26,6 +26,7 @@
None
+ $(AndroidFrameworkVersion)
true
true
bin\Release