diff --git a/src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaConstructorInfo.cs b/src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaConstructorInfo.cs index c5581add361..9b62c0534bc 100644 --- a/src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaConstructorInfo.cs +++ b/src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaConstructorInfo.cs @@ -42,7 +42,7 @@ public override unsafe object Invoke (IJavaPeerable self, JniArgumentValue* argu { if (self == null) { var h = members.InstanceMethods.StartCreateInstance (JniSignature, typeof (JavaInstanceProxy), arguments); - self = JniEnvironment.Runtime.ValueManager.GetObject (ref h, JniObjectReferenceOptions.CopyAndDispose); + self = JniEnvironment.Runtime.ValueManager.GetValue (ref h, JniObjectReferenceOptions.CopyAndDispose); } members.InstanceMethods.FinishCreateInstance (JniSignature, self, arguments); return new DynamicJavaInstance (self); diff --git a/src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaMemberInfo.cs b/src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaMemberInfo.cs index 361fb8c3aac..51e56adcd22 100644 --- a/src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaMemberInfo.cs +++ b/src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaMemberInfo.cs @@ -22,7 +22,7 @@ protected virtual void Dispose (bool disposing) protected static object ToReturnValue (ref JniObjectReference handle, string signature, int n) { - var instance = JniEnvironment.Runtime.ValueManager.GetObject (ref handle, JniObjectReferenceOptions.CopyAndDispose); + var instance = JniEnvironment.Runtime.ValueManager.GetValue (ref handle, JniObjectReferenceOptions.CopyAndDispose); switch (signature [n]) { case 'L': return new DynamicJavaInstance (instance); diff --git a/src/Java.Interop.Export/Java.Interop/ExportedMemberBuilder.cs b/src/Java.Interop.Export/Java.Interop/ExportedMemberBuilder.cs index b4965e70e1e..9b55f75bd84 100644 --- a/src/Java.Interop.Export/Java.Interop/ExportedMemberBuilder.cs +++ b/src/Java.Interop.Export/Java.Interop/ExportedMemberBuilder.cs @@ -328,7 +328,7 @@ static Expression GetThis (Expression vm, Type targetType, Expression context) { return Expression.Call ( Expression.Property (vm, "ValueManager"), - "GetObject", + "GetValue", new[]{targetType}, context); } diff --git a/src/Java.Interop.Export/Tests/Java.Interop/ExportedMemberBuilderTest.cs b/src/Java.Interop.Export/Tests/Java.Interop/ExportedMemberBuilderTest.cs index 1ed89abd697..36ef4265a2e 100644 --- a/src/Java.Interop.Export/Tests/Java.Interop/ExportedMemberBuilderTest.cs +++ b/src/Java.Interop.Export/Tests/Java.Interop/ExportedMemberBuilderTest.cs @@ -178,7 +178,7 @@ public void CreateMarshalFromJniMethodExpression_InstanceAction () ExportTest __this; __jvm = JniEnvironment.Runtime; - __this = __jvm.ValueManager.GetObject(__context); + __this = __jvm.ValueManager.GetValue(__context); __this.InstanceAction(); } catch (Exception __e) @@ -304,7 +304,7 @@ public void CreateMarshalFromJniMethodExpression_FuncInt64 () long __mret; __jvm = JniEnvironment.Runtime; - __this = __jvm.ValueManager.GetObject(__context); + __this = __jvm.ValueManager.GetValue(__context); __mret = __this.FuncInt64(); __jret = __mret; return __jret; @@ -343,7 +343,7 @@ public void CreateMarshalFromJniMethodExpression_FuncIJavaObject () JavaObject __mret; __jvm = JniEnvironment.Runtime; - __this = __jvm.ValueManager.GetObject(__context); + __this = __jvm.ValueManager.GetValue(__context); __mret = __this.FuncIJavaObject(); __jret = References.NewReturnToJniRef(__mret); return __jret; diff --git a/src/Java.Interop/Java.Interop/JavaArray.cs b/src/Java.Interop/Java.Interop/JavaArray.cs index 7b4931c043a..61b16c2a868 100644 --- a/src/Java.Interop/Java.Interop/JavaArray.cs +++ b/src/Java.Interop/Java.Interop/JavaArray.cs @@ -129,12 +129,6 @@ internal static Exception CreateMarshalNotSupportedException (Type sourceType, T internal static IList CreateValue (ref JniObjectReference reference, JniObjectReferenceOptions transfer, Type targetType, ArrayCreator creator) where TArray : JavaArray { - var value = JniEnvironment.Runtime.ValueManager.PeekObject (reference); - var array = value as TArray; - if (array != null) { - JniObjectReference.Dispose (ref reference, transfer); - return array.ToTargetType (targetType, dispose: false); - } return creator (ref reference, transfer) .ToTargetType (targetType, dispose: true); } diff --git a/src/Java.Interop/Java.Interop/JavaPeerableExtensions.cs b/src/Java.Interop/Java.Interop/JavaPeerableExtensions.cs index f386498c393..2b428d77cd2 100644 --- a/src/Java.Interop/Java.Interop/JavaPeerableExtensions.cs +++ b/src/Java.Interop/Java.Interop/JavaPeerableExtensions.cs @@ -9,18 +9,5 @@ public static string GetJniTypeName (this IJavaPeerable self) JniPeerMembers.AssertSelf (self); return JniEnvironment.Types.GetJniTypeNameFromInstance (self.PeerReference); } - - internal static object GetValue (ref JniObjectReference handle, JniObjectReferenceOptions transfer, Type targetType) - { - return JniEnvironment.Runtime.ValueManager.GetObject (ref handle, transfer, targetType); - } - - internal static JniObjectReference CreateLocalRef (object value) - { - var o = value as IJavaPeerable; - if (o == null || !o.PeerReference.IsValid) - return new JniObjectReference (); - return o.PeerReference.NewLocalRef (); - } } } diff --git a/src/Java.Interop/Java.Interop/JavaProxyObject.cs b/src/Java.Interop/Java.Interop/JavaProxyObject.cs index 693e726530a..79c6a2801ce 100644 --- a/src/Java.Interop/Java.Interop/JavaProxyObject.cs +++ b/src/Java.Interop/Java.Interop/JavaProxyObject.cs @@ -69,20 +69,21 @@ public static JavaProxyObject GetProxy (object value) static bool _Equals (IntPtr jnienv, IntPtr n_self, IntPtr n_value) { - var self = JniEnvironment.Runtime.ValueManager.GetObject (n_self); - var value = JniEnvironment.Runtime.ValueManager.GetObject (n_value); + var self = (JavaProxyObject) JniEnvironment.Runtime.ValueManager.PeekObject (new JniObjectReference (n_self)); + var r_value = new JniObjectReference (n_value); + var value = JniEnvironment.Runtime.ValueManager.GetValue (ref r_value, JniObjectReferenceOptions.Copy); return self.Equals (value); } static int _GetHashCode (IntPtr jnienv, IntPtr n_self) { - var self = JniEnvironment.Runtime.ValueManager.GetObject (n_self); + var self = (JavaProxyObject) JniEnvironment.Runtime.ValueManager.PeekObject (new JniObjectReference (n_self)); return self.GetHashCode (); } static IntPtr _ToString (IntPtr jnienv, IntPtr n_self) { - var self = JniEnvironment.Runtime.ValueManager.GetObject (n_self); + var self = (JavaProxyObject) JniEnvironment.Runtime.ValueManager.PeekObject (new JniObjectReference (n_self)); var s = self.ToString (); var r = JniEnvironment.Strings.NewString (s); try { diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs index 2d4c4bdddf7..3ebd83b44e4 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs @@ -75,12 +75,21 @@ internal void RegisterObject (T value) lock (RegisteredInstances) { WeakReference existing; IJavaPeerable target; - if (RegisteredInstances.TryGetValue (key, out existing) && (target = (IJavaPeerable) existing.Target) != null) - throw new NotSupportedException ( - string.Format ("Cannot register instance {0}(0x{1}), as an instance with the same handle {2}(0x{3}) has already been registered.", - value.GetType ().FullName, value.PeerReference.ToString (), - target.GetType ().FullName, target.PeerReference.ToString ())); - RegisteredInstances [key] = new WeakReference (value, trackResurrection: true); + if (RegisteredInstances.TryGetValue (key, out existing) && (target = (IJavaPeerable)existing.Target) != null) + Runtime.ObjectReferenceManager.WriteGlobalReferenceLine ( + "Warning: Not registering PeerReference={0} IdentityHashCode=0x{1} Instance={2} Instance.Type={3} Java.Type={4}; " + + "keeping previously registered PeerReference={5} Instance={6} Instance.Type={7} Java.Type={8}.", + value.PeerReference.ToString (), + key.ToString ("x"), + RuntimeHelpers.GetHashCode (value).ToString ("x"), + value.GetType ().FullName, + JniEnvironment.Types.GetJniTypeNameFromInstance (value.PeerReference), + target.PeerReference.ToString (), + RuntimeHelpers.GetHashCode (target).ToString ("x"), + target.GetType ().FullName, + JniEnvironment.Types.GetJniTypeNameFromInstance (target.PeerReference)); + else + RegisteredInstances [key] = new WeakReference (value, trackResurrection: true); } value.Registered = true; } @@ -238,40 +247,71 @@ internal protected virtual bool TryGC (IJavaPeerable value, ref JniObjectReferen return !handle.IsValid; } - public IJavaPeerable PeekObject (JniObjectReference reference) + internal protected virtual IJavaPeerable PeekObject (JniObjectReference reference) { if (!reference.IsValid) return null; + int key = JniSystem.IdentityHashCode (reference); + + WeakReference wv; lock (RegisteredInstances) { - WeakReference wv; - if (RegisteredInstances.TryGetValue (key, out wv)) { - IJavaPeerable t = (IJavaPeerable) wv.Target; - if (t != null) - return t; + if (!RegisteredInstances.TryGetValue (key, out wv)) { RegisteredInstances.Remove (key); } } - return null; + return wv == null ? null : (IJavaPeerable) wv.Target; } - public IJavaPeerable GetObject (ref JniObjectReference reference, JniObjectReferenceOptions transfer, Type targetType = null) + public object PeekValue (JniObjectReference reference) { if (!reference.IsValid) return null; - var existing = PeekObject (reference); - if (existing != null && (targetType == null || targetType.GetTypeInfo ().IsAssignableFrom (existing.GetType ().GetTypeInfo ()))) { - JniObjectReference.Dispose (ref reference, transfer); - return existing; - } + var t = PeekObject (reference); + var b = Unbox (t); + if (b != null) + return b; + return t; + } - return CreateObjectWrapper (ref reference, transfer, targetType); + static object Unbox (IJavaPeerable value) + { + var p = value as JavaProxyObject; + if (p != null) + return p.Value; + var x = value as JavaProxyThrowable; + if (x != null) + return x.Exception; + return null; } - protected virtual IJavaPeerable CreateObjectWrapper (ref JniObjectReference reference, JniObjectReferenceOptions transfer, Type targetType) + object PeekBoxedObject (JniObjectReference reference) + { + var t = PeekObject (reference); + return Unbox (t); + } + + static readonly KeyValuePair[] WrapperTypeMappings = new []{ + new KeyValuePair(typeof (object), typeof (JavaObject)), + new KeyValuePair(typeof (IJavaPeerable), typeof (JavaObject)), + new KeyValuePair(typeof (Exception), typeof (JavaException)), + }; + + static Type GetWrapperType (Type type) + { + foreach (var m in WrapperTypeMappings) { + if (m.Key == type) + return m.Value; + } + return type; + } + + internal protected virtual IJavaPeerable CreateObject (ref JniObjectReference reference, JniObjectReferenceOptions transfer, Type targetType) { targetType = targetType ?? typeof (JavaObject); + targetType = GetWrapperType (targetType); + if (!typeof (IJavaPeerable).GetTypeInfo ().IsAssignableFrom (targetType.GetTypeInfo ())) throw new ArgumentException ("targetType must implement IJavaPeerable!", "targetType"); @@ -334,24 +374,66 @@ static ConstructorInfo GetActivationConstructor (Type type) .FirstOrDefault (); } - public T GetObject (ref JniObjectReference reference, JniObjectReferenceOptions transfer) - where T : IJavaPeerable + + public object CreateValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType = null) { - return (T) GetObject (ref reference, transfer, typeof (T)); + if (!reference.IsValid) + return null; + + if (targetType != null && typeof (IJavaPeerable).GetTypeInfo ().IsAssignableFrom (targetType.GetTypeInfo ())) { + return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); + } + + var boxed = PeekBoxedObject (reference); + if (boxed != null) { + JniObjectReference.Dispose (ref reference, options); + if (targetType != null) + return Convert.ChangeType (boxed, targetType); + return boxed; + } + + targetType = targetType ?? GetRuntimeType (reference); + if (targetType == null) { + // Let's hope this is an IJavaPeerable! + return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); + } + var marshaler = GetValueMarshaler (targetType); + return marshaler.CreateValue (ref reference, options, targetType); } - public IJavaPeerable GetObject (IntPtr jniHandle, Type targetType = null) + public T CreateValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType = null) { - if (jniHandle == IntPtr.Zero) - return null; - var h = new JniObjectReference (jniHandle); - return GetObject (ref h, JniObjectReferenceOptions.Copy, targetType); + if (!reference.IsValid) + return default (T); + + if (targetType != null && !typeof (T).GetTypeInfo ().IsAssignableFrom (targetType.GetTypeInfo ())) + throw new ArgumentException ( + string.Format ("Requested runtime '{0}' value of '{1}' is not compatible with requested compile-time type T of '{2}'.", + nameof (targetType), + targetType, + typeof (T)), + nameof (targetType)); + + var boxed = PeekBoxedObject (reference); + if (boxed != null) { + JniObjectReference.Dispose (ref reference, options); + return (T) Convert.ChangeType (boxed, targetType ?? typeof (T)); + } + + targetType = targetType ?? typeof (T); + + if (typeof (IJavaPeerable).GetTypeInfo ().IsAssignableFrom (targetType.GetTypeInfo ())) { + return (T) JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); + } + + var marshaler = GetValueMarshaler (); + return marshaler.CreateGenericValue (ref reference, options, targetType); } - public T GetObject (IntPtr jniHandle) - where T : IJavaPeerable + internal Type GetRuntimeType (JniObjectReference reference) { - return (T) GetObject (jniHandle, typeof(T)); + var signature = Runtime.TypeManager.GetTypeSignature (JniEnvironment.Types.GetJniTypeNameFromInstance (reference)); + return Runtime.TypeManager.GetType (signature); } public object GetValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType = null) @@ -359,24 +441,29 @@ public object GetValue (ref JniObjectReference reference, JniObjectReferenceOpti if (!reference.IsValid) return null; - var target = PeekObject (reference); - var proxy = target as JavaProxyObject; - if (proxy != null) { + var existing = PeekValue (reference); + if (existing != null && (targetType == null || targetType.GetTypeInfo ().IsAssignableFrom (existing.GetType ().GetTypeInfo ()))) { JniObjectReference.Dispose (ref reference, options); - return proxy.Value; + return existing; } - if (target != null && (targetType == null || targetType.GetTypeInfo ().IsAssignableFrom (target.GetType ().GetTypeInfo ()))) { - JniObjectReference.Dispose (ref reference, options); - return target; + if (targetType != null && typeof (IJavaPeerable).GetTypeInfo ().IsAssignableFrom (targetType.GetTypeInfo ())) { + return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); } + targetType = targetType ?? GetRuntimeType (reference); if (targetType == null) { - var signature = Runtime.TypeManager.GetTypeSignature (JniEnvironment.Types.GetJniTypeNameFromInstance (reference)); - targetType = Runtime.TypeManager.GetType (signature); + // Let's hope this is an IJavaPeerable! + return JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); } - var vm = GetValueMarshaler (targetType); - return vm.CreateValue (ref reference, options, targetType); + var marshaler = GetValueMarshaler (targetType); + return marshaler.CreateValue (ref reference, options, targetType); + } + + public T GetValue (IntPtr handle) + { + var r = new JniObjectReference (handle); + return GetValue (ref r, JniObjectReferenceOptions.Copy); } public T GetValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType = null) @@ -384,20 +471,28 @@ public T GetValue (ref JniObjectReference reference, JniObjectReferenceOption if (!reference.IsValid) return default (T); - var target = PeekObject (reference); - var proxy = target as JavaProxyObject; - if (proxy != null) { + if (targetType != null && !typeof (T).GetTypeInfo ().IsAssignableFrom (targetType.GetTypeInfo ())) + throw new ArgumentException ( + string.Format ("Requested runtime '{0}' value of '{1}' is not compatible with requested compile-time type T of '{2}'.", + nameof (targetType), + targetType, + typeof (T)), + nameof (targetType)); + + targetType = targetType ?? typeof (T); + + var existing = PeekValue (reference); + if (existing != null && (targetType == null || targetType.GetTypeInfo ().IsAssignableFrom (existing.GetType ().GetTypeInfo ()))) { JniObjectReference.Dispose (ref reference, options); - return (T) proxy.Value; + return (T) existing; } - if (target is T) { - JniObjectReference.Dispose (ref reference, options); - return (T) target; + if (typeof (IJavaPeerable).GetTypeInfo ().IsAssignableFrom (targetType.GetTypeInfo ())) { + return (T) JavaPeerableValueMarshaler.Instance.CreateGenericValue (ref reference, options, targetType); } - var vm = GetValueMarshaler (); - return vm.CreateGenericValue (ref reference, options, typeof (T)); + var marshaler = GetValueMarshaler (); + return marshaler.CreateGenericValue (ref reference, options, targetType); } Dictionary Marshalers = new Dictionary (); @@ -462,12 +557,6 @@ protected virtual JniValueMarshaler GetValueMarshalerCore (Type type) { return ProxyValueMarshaler.Instance; } - - static TDelegate CreateMethodDelegate(Type type, string methodName) - where TDelegate : class - { - return (TDelegate) (object) type.GetTypeInfo ().GetDeclaredMethod (methodName).CreateDelegate (typeof (TDelegate)); - } } } @@ -477,7 +566,11 @@ class JavaPeerableValueMarshaler : JniValueMarshaler { public override IJavaPeerable CreateGenericValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType) { - return JniEnvironment.Runtime.ValueManager.GetObject (ref reference, options, targetType); + var jvm = JniEnvironment.Runtime; + var marshaler = jvm.ValueManager.GetValueMarshaler (targetType ?? typeof(IJavaPeerable)); + if (marshaler != Instance) + return (IJavaPeerable) marshaler.CreateValue (ref reference, options, targetType); + return jvm.ValueManager.CreateObject (ref reference, options, targetType); } public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState (IJavaPeerable value, ParameterAttributes synchronize) @@ -512,7 +605,6 @@ public override T CreateGenericValue (ref JniObjectReference reference, JniObjec public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState (T value, ParameterAttributes synchronize) { - System.Diagnostics.Debug.WriteLine ("# jonp: DelegatingValueMarshaler.CreateGenericObjectReferenceArgumentState: ValueMarshaler={0}; synchronize={1}", ValueMarshaler.GetType (), synchronize); return ValueMarshaler.CreateObjectReferenceArgumentState (value, synchronize); } @@ -531,8 +623,7 @@ public override object CreateGenericValue (ref JniObjectReference reference, Jni var jvm = JniEnvironment.Runtime; if (targetType == null || targetType == typeof (object)) { - var signature = jvm.TypeManager.GetTypeSignature (JniEnvironment.Types.GetJniTypeNameFromInstance (reference)); - targetType = jvm.TypeManager.GetType (signature); + targetType = jvm.ValueManager.GetRuntimeType (reference); } if (targetType != null) { var vm = jvm.ValueManager.GetValueMarshaler (targetType); @@ -541,16 +632,13 @@ public override object CreateGenericValue (ref JniObjectReference reference, Jni } } - var target = jvm.ValueManager.PeekObject (reference); - - JniObjectReference.Dispose (ref reference, options); - - var proxy = target as JavaProxyObject; - if (proxy != null) { + var target = jvm.ValueManager.PeekValue (reference); + if (target != null) { JniObjectReference.Dispose (ref reference, options); - return proxy.Value; + return target; } - return null; + // Punt! Hope it's a java.lang.Object + return jvm.ValueManager.CreateObject (ref reference, options, targetType); } public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState (object value, ParameterAttributes synchronize) diff --git a/src/Java.Interop/Java.Interop/JniRuntime.cs b/src/Java.Interop/Java.Interop/JniRuntime.cs index 8f3c4e2b333..7fcf41fc70d 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.cs @@ -326,21 +326,12 @@ public void DestroyRuntime () Invoker.DestroyJavaVM (InvocationPointer); } - public virtual Exception GetExceptionForThrowable (ref JniObjectReference value, JniObjectReferenceOptions transfer) + public virtual Exception GetExceptionForThrowable (ref JniObjectReference reference, JniObjectReferenceOptions options) { #if XA_INTEGRATION throw new NotSupportedException ("Do not know h ow to convert a JniObjectReference to a System.Exception!"); #else // !XA_INTEGRATION - var o = ValueManager.PeekObject (value); - var e = o as JavaException; - if (e != null) { - JniObjectReference.Dispose (ref value, transfer); - var p = e as JavaProxyThrowable; - if (p != null) - return p.Exception; - return e; - } - return ValueManager.GetObject (ref value, transfer); + return ValueManager.GetValue (ref reference, options); #endif // !̣XA_INTEGRATION } diff --git a/src/Java.Interop/Tests/Interop-Tests.projitems b/src/Java.Interop/Tests/Interop-Tests.projitems index 3551447c87d..d7f5dc412fd 100644 --- a/src/Java.Interop/Tests/Interop-Tests.projitems +++ b/src/Java.Interop/Tests/Interop-Tests.projitems @@ -48,6 +48,7 @@ + diff --git a/src/Java.Interop/Tests/Java.Interop/CallVirtualFromConstructorDerived.cs b/src/Java.Interop/Tests/Java.Interop/CallVirtualFromConstructorDerived.cs index 51e92d77610..2347d5c4e1e 100644 --- a/src/Java.Interop/Tests/Java.Interop/CallVirtualFromConstructorDerived.cs +++ b/src/Java.Interop/Tests/Java.Interop/CallVirtualFromConstructorDerived.cs @@ -40,7 +40,8 @@ public override void CalledFromConstructor (int value) static void CalledFromConstructorHandler (IntPtr jnienv, IntPtr n_self, int value) { - var self = JniEnvironment.Runtime.ValueManager.GetObject(n_self); + var r_self = new JniObjectReference (n_self); + var self = JniEnvironment.Runtime.ValueManager.GetValue(ref r_self, JniObjectReferenceOptions.Copy); self.CalledFromConstructor (value); self.DisposeUnlessRegistered (); } diff --git a/src/Java.Interop/Tests/Java.Interop/InvokeVirtualFromConstructorTests.cs b/src/Java.Interop/Tests/Java.Interop/InvokeVirtualFromConstructorTests.cs index e37793365c4..dca92da8e68 100644 --- a/src/Java.Interop/Tests/Java.Interop/InvokeVirtualFromConstructorTests.cs +++ b/src/Java.Interop/Tests/Java.Interop/InvokeVirtualFromConstructorTests.cs @@ -14,7 +14,7 @@ public void InvokeVirtualFromConstructor () { using (var t = new CallVirtualFromConstructorDerived (42)) { Assert.IsTrue (t.Called); - Assert.IsNotNull (JniRuntime.CurrentRuntime.ValueManager.PeekObject (t.PeerReference)); + Assert.IsNotNull (JniRuntime.CurrentRuntime.ValueManager.PeekValue (t.PeerReference)); } } } diff --git a/src/Java.Interop/Tests/Java.Interop/JavaObjectTest.cs b/src/Java.Interop/Tests/Java.Interop/JavaObjectTest.cs index 9ff9c846982..b958dcead08 100644 --- a/src/Java.Interop/Tests/Java.Interop/JavaObjectTest.cs +++ b/src/Java.Interop/Tests/Java.Interop/JavaObjectTest.cs @@ -29,9 +29,9 @@ public void JavaReferencedInstanceSurvivesCollection () GC.WaitForPendingFinalizers (); GC.WaitForPendingFinalizers (); var first = array [0]; - Assert.IsNotNull (JniRuntime.CurrentRuntime.ValueManager.PeekObject (first.PeerReference)); + Assert.IsNotNull (JniRuntime.CurrentRuntime.ValueManager.PeekValue (first.PeerReference)); var f = first.PeerReference; - var o = (JavaObject) JniRuntime.CurrentRuntime.ValueManager.GetObject (ref f, JniObjectReferenceOptions.Copy); + var o = (JavaObject) JniRuntime.CurrentRuntime.ValueManager.GetValue (ref f, JniObjectReferenceOptions.Copy); Assert.AreSame (first, o); if (oldHandle != o.PeerReference.Handle) { Console.WriteLine ("Yay, object handle changed; value survived a GC!"); @@ -53,22 +53,23 @@ public void UnregisterFromRuntime () l = o.PeerReference.NewLocalRef (); Assert.AreEqual (JniObjectReferenceType.Global, o.PeerReference.Type); Assert.AreEqual (registeredCount+1, JniRuntime.CurrentRuntime.ValueManager.GetSurfacedObjects ().Count); - Assert.IsNotNull (JniRuntime.CurrentRuntime.ValueManager.PeekObject (l)); + Assert.IsNotNull (JniRuntime.CurrentRuntime.ValueManager.PeekValue (l)); Assert.AreNotSame (l, o.PeerReference); - Assert.AreSame (o, JniRuntime.CurrentRuntime.ValueManager.PeekObject (l)); + Assert.AreSame (o, JniRuntime.CurrentRuntime.ValueManager.PeekValue (l)); } Assert.AreEqual (registeredCount, JniRuntime.CurrentRuntime.ValueManager.GetSurfacedObjects ().Count); - Assert.IsNull (JniRuntime.CurrentRuntime.ValueManager.PeekObject (l)); + Assert.IsNull (JniRuntime.CurrentRuntime.ValueManager.PeekValue (l)); JniObjectReference.Dispose (ref l); Assert.Throws (() => o.UnregisterFromRuntime ()); } [Test] - public void RegisterWithVM_ThrowsOnDuplicateEntry () + public void RegisterWithVM_PermitsAliases () { using (var original = new JavaObject ()) { var p = original.PeerReference; - Assert.Throws (() => new JavaObject (ref p, JniObjectReferenceOptions.Copy)); + var alias = new JavaObject (ref p, JniObjectReferenceOptions.Copy); + alias.Dispose (); } } @@ -89,7 +90,7 @@ public void UnreferencedInstanceIsCollected () GC.WaitForPendingFinalizers (); Assert.IsFalse (r.IsAlive); Assert.IsNull (r.Target); - Assert.IsNull (JniRuntime.CurrentRuntime.ValueManager.PeekObject (oldHandle)); + Assert.IsNull (JniRuntime.CurrentRuntime.ValueManager.PeekValue (oldHandle)); JniObjectReference.Dispose (ref oldHandle); } diff --git a/src/Java.Interop/Tests/Java.Interop/JniRuntime.JniValueManagerTests.cs b/src/Java.Interop/Tests/Java.Interop/JniRuntime.JniValueManagerTests.cs new file mode 100644 index 00000000000..ea88c589c9e --- /dev/null +++ b/src/Java.Interop/Tests/Java.Interop/JniRuntime.JniValueManagerTests.cs @@ -0,0 +1,108 @@ +using System; + +using Java.Interop; + +using NUnit.Framework; + +namespace Java.InteropTests { + + [TestFixture] + public class JniRuntimeJniValueManagerTests : JavaVMFixture { + + [Test] + public void CreateValue () + { + using (var vm = new JniRuntime.JniValueManager ()) + using (var o = new JavaObject ()) { + vm.OnSetRuntime (JniRuntime.CurrentRuntime); + + var r = o.PeerReference; + var x = (IJavaPeerable) vm.CreateValue (ref r, JniObjectReferenceOptions.Copy); + Assert.AreNotSame (o, x); + x.Dispose (); + + x = vm.CreateValue (ref r, JniObjectReferenceOptions.Copy); + Assert.AreNotSame (o, x); + x.Dispose (); + } + } + + [Test] + public void GetValue_ReturnsAlias () + { + var local = new JavaObject (); + local.UnregisterFromRuntime (); + Assert.IsNull (JniRuntime.CurrentRuntime.ValueManager.PeekValue (local.PeerReference)); + // GetObject must always return a value (unless handle is null, etc.). + // However, since we called local.UnregisterFromRuntime(), + // JniRuntime.PeekObject() is null (asserted above), but GetObject() must + // **still** return _something_. + // In this case, it returns an _alias_. + // TODO: "most derived type" alias generation. (Not relevant here, but...) + var p = local.PeerReference; + var alias = JniRuntime.CurrentRuntime.ValueManager.GetValue (ref p, JniObjectReferenceOptions.Copy); + Assert.AreNotSame (local, alias); + alias.Dispose (); + local.Dispose (); + } + + [Test] + public void GetValue_ReturnsNullWithNullHandle () + { + var r = new JniObjectReference (); + var o = JniRuntime.CurrentRuntime.ValueManager.GetValue (ref r, JniObjectReferenceOptions.Copy); + Assert.IsNull (o); + } + + [Test] + public void PeekValue () + { + JniObjectReference lref; + using (var o = new JavaObject ()) { + lref = o.PeerReference.NewLocalRef (); + Assert.AreSame (o, JniRuntime.CurrentRuntime.ValueManager.PeekValue (lref)); + } + // At this point, the Java-side object is kept alive by `lref`, + // but the wrapper instance has been disposed, and thus should + // be unregistered, and thus unfindable. + Assert.IsNull (JniRuntime.CurrentRuntime.ValueManager.PeekValue (lref)); + JniObjectReference.Dispose (ref lref); + } + + public void PeekValue_BoxedObjects () + { + var vm = JniRuntime.CurrentRuntime.ValueManager; + var marshaler = vm.GetValueMarshaler (); + var ad = AppDomain.CurrentDomain; + + var proxy = marshaler.CreateGenericArgumentState (ad); + Assert.AreSame (ad, vm.PeekValue (proxy.ReferenceValue)); + marshaler.DestroyGenericArgumentState (ad, ref proxy); + + var ex = new InvalidOperationException ("boo!"); + proxy = marshaler.CreateGenericArgumentState (ex); + Assert.AreSame (ex, vm.PeekValue (proxy.ReferenceValue)); + marshaler.DestroyGenericArgumentState (ex, ref proxy); + } + + [Test] + public void GetValue_ReturnsNullWithInvalidSafeHandle () + { + var invalid = new JniObjectReference (); + Assert.IsNull (JniRuntime.CurrentRuntime.ValueManager.GetValue (ref invalid, JniObjectReferenceOptions.CopyAndDispose)); + } + + [Test] + public unsafe void GetValue_FindBestMatchType () + { + using (var t = new JniType (TestType.JniTypeName)) { + var c = t.GetConstructor ("()V"); + var o = t.NewObject (c, null); + using (var w = JniRuntime.CurrentRuntime.ValueManager.GetValue (ref o, JniObjectReferenceOptions.CopyAndDispose)) { + Assert.AreEqual (typeof (TestType), w.GetType ()); + } + } + } + } +} + diff --git a/src/Java.Interop/Tests/Java.Interop/JniRuntimeTest.cs b/src/Java.Interop/Tests/Java.Interop/JniRuntimeTest.cs index e0af9e1941a..6aa60ee2a72 100644 --- a/src/Java.Interop/Tests/Java.Interop/JniRuntimeTest.cs +++ b/src/Java.Interop/Tests/Java.Interop/JniRuntimeTest.cs @@ -51,66 +51,6 @@ public void GetRegisteredJavaVM_ExistingInstance () { Assert.AreEqual (JniRuntime.CurrentRuntime, JniRuntime.GetRegisteredRuntime (JniRuntime.CurrentRuntime.InvocationPointer)); } - - [Test] - public void GetObject_ReturnsAlias () - { - var local = new JavaObject (); - local.UnregisterFromRuntime (); - Assert.IsNull (JniRuntime.CurrentRuntime.ValueManager.PeekObject (local.PeerReference)); - // GetObject must always return a value (unless handle is null, etc.). - // However, since we called local.UnregisterFromRuntime(), - // JniRuntime.PeekObject() is null (asserted above), but GetObject() must - // **still** return _something_. - // In this case, it returns an _alias_. - // TODO: "most derived type" alias generation. (Not relevant here, but...) - var p = local.PeerReference; - var alias = JniRuntime.CurrentRuntime.ValueManager.GetObject (ref p, JniObjectReferenceOptions.Copy); - Assert.AreNotSame (local, alias); - alias.Dispose (); - local.Dispose (); - } - - [Test] - public void GetObject_ReturnsNullWithNullHandle () - { - var o = JniRuntime.CurrentRuntime.ValueManager.GetObject (IntPtr.Zero); - Assert.IsNull (o); - } - - [Test] - public void GetObject_ReturnsRegisteredInstance () - { - JniObjectReference lref; - using (var o = new JavaObject ()) { - lref = o.PeerReference.NewLocalRef (); - Assert.AreSame (o, JniRuntime.CurrentRuntime.ValueManager.PeekObject (lref)); - } - // At this point, the Java-side object is kept alive by `lref`, - // but the wrapper instance has been disposed, and thus should - // be unregistered, and thus unfindable. - Assert.IsNull (JniRuntime.CurrentRuntime.ValueManager.PeekObject (lref)); - JniObjectReference.Dispose (ref lref); - } - - [Test] - public void GetObject_ReturnsNullWithInvalidSafeHandle () - { - var invalid = new JniObjectReference (); - Assert.IsNull (JniRuntime.CurrentRuntime.ValueManager.GetObject (ref invalid, JniObjectReferenceOptions.CopyAndDispose)); - } - - [Test] - public unsafe void GetObject_FindBestMatchType () - { - using (var t = new JniType (TestType.JniTypeName)) { - var c = t.GetConstructor ("()V"); - var o = t.NewObject (c, null); - using (var w = JniRuntime.CurrentRuntime.ValueManager.GetObject (ref o, JniObjectReferenceOptions.CopyAndDispose)) { - Assert.AreEqual (typeof (TestType), w.GetType ()); - } - } - } } } diff --git a/src/Java.Interop/Tests/Java.Interop/JniValueMarshalerContractTests.cs b/src/Java.Interop/Tests/Java.Interop/JniValueMarshalerContractTests.cs index 209f29579d9..c64fccb33ff 100644 --- a/src/Java.Interop/Tests/Java.Interop/JniValueMarshalerContractTests.cs +++ b/src/Java.Interop/Tests/Java.Interop/JniValueMarshalerContractTests.cs @@ -14,6 +14,7 @@ public abstract class JniValueMarshalerContractTests : JavaVMFixture { protected abstract T Value {get;} protected virtual bool IsJniValueType {get;} + protected virtual bool UsesProxy {get;} protected virtual bool Equals (T x, T y) { @@ -170,6 +171,8 @@ public void CreateValue () var r = s.ReferenceValue; var o = marshaler.CreateValue (ref r, JniObjectReferenceOptions.Copy); Assert.IsTrue (Equals (Value, (T) o)); + if (!UsesProxy && !typeof(T).IsValueType) + Assert.AreNotSame (Value, o); marshaler.DestroyArgumentState (Value, ref s); Dispose ((T) o); @@ -182,6 +185,8 @@ public void CreateGenericValue () var r = s.ReferenceValue; var o = marshaler.CreateGenericValue (ref r, JniObjectReferenceOptions.Copy); Assert.IsTrue (Equals (Value, o)); + if (!UsesProxy && !typeof(T).IsValueType) + Assert.AreNotSame (Value, o); marshaler.DestroyGenericArgumentState (Value, ref s); Dispose (o); @@ -384,6 +389,7 @@ public class JniValueMarshaler_object_ContractTests : JniValueMarshalerContractT readonly object value = new object (); protected override object Value {get {return value;}} + protected override bool UsesProxy {get {return true;}} [Test] public void SpecificTypesAreUsed () diff --git a/src/Java.Interop/Tests/Java.Interop/TestType.cs b/src/Java.Interop/Tests/Java.Interop/TestType.cs index ed97b0929f3..3d43f9db40c 100644 --- a/src/Java.Interop/Tests/Java.Interop/TestType.cs +++ b/src/Java.Interop/Tests/Java.Interop/TestType.cs @@ -87,9 +87,9 @@ static Delegate GetEqualsThisHandler () Func h = (jnienv, n_self, n_value) => { var jvm = JniEnvironment.Runtime; var r_self = new JniObjectReference (n_self); - var self = jvm.ValueManager.GetObject(ref r_self, JniObjectReferenceOptions.CopyAndDoNotRegister); + var self = jvm.ValueManager.GetValue(ref r_self, JniObjectReferenceOptions.CopyAndDoNotRegister); var r_value = new JniObjectReference (n_self); - var value = jvm.ValueManager.GetObject (ref r_value, JniObjectReferenceOptions.CopyAndDoNotRegister); + var value = jvm.ValueManager.GetValue (ref r_value, JniObjectReferenceOptions.CopyAndDoNotRegister); try { return self.EqualsThis (value); @@ -105,7 +105,7 @@ static Delegate GetInt32ValueHandler () { Func h = (jnienv, n_self) => { var r_self = new JniObjectReference (n_self); - var self = JniEnvironment.Runtime.ValueManager.GetObject(ref r_self, JniObjectReferenceOptions.CopyAndDoNotRegister); + var self = JniEnvironment.Runtime.ValueManager.GetValue(ref r_self, JniObjectReferenceOptions.CopyAndDoNotRegister); try { return self.GetInt32Value (); } finally { @@ -124,7 +124,7 @@ static Delegate _GetStringValueHandler () static IntPtr GetStringValueHandler (IntPtr jnienv, IntPtr n_self, int value) { var r_self = new JniObjectReference (n_self); - var self = JniEnvironment.Runtime.ValueManager.GetObject(ref r_self, JniObjectReferenceOptions.CopyAndDoNotRegister); + var self = JniEnvironment.Runtime.ValueManager.GetValue(ref r_self, JniObjectReferenceOptions.CopyAndDoNotRegister); try { var s = self.GetStringValue (value); var r = JniEnvironment.Strings.NewString (s); @@ -141,7 +141,7 @@ static IntPtr GetStringValueHandler (IntPtr jnienv, IntPtr n_self, int value) static void MethodThrowsHandler (IntPtr jnienv, IntPtr n_self) { var r_self = new JniObjectReference (n_self); - var self = JniEnvironment.Runtime.ValueManager.GetObject(ref r_self, JniObjectReferenceOptions.CopyAndDoNotRegister); + var self = JniEnvironment.Runtime.ValueManager.GetValue(ref r_self, JniObjectReferenceOptions.CopyAndDoNotRegister); try { self.MethodThrows (); } finally { diff --git a/tests/PerformanceTests/JavaTiming.cs b/tests/PerformanceTests/JavaTiming.cs index 1551134f354..bcb2e183bd4 100644 --- a/tests/PerformanceTests/JavaTiming.cs +++ b/tests/PerformanceTests/JavaTiming.cs @@ -56,7 +56,7 @@ public static IJavaPeerable StaticObjectMethod () { TypeRef.GetCachedStaticMethod (ref som, "StaticObjectMethod", "()Ljava/lang/Object;"); var lref = JniEnvironment.StaticMethods.CallStaticObjectMethod (TypeRef.PeerReference, som); - return JniEnvironment.Runtime.ValueManager.GetObject (ref lref, JniObjectReferenceOptions.CopyAndDispose); + return JniEnvironment.Runtime.ValueManager.GetValue (ref lref, JniObjectReferenceOptions.CopyAndDispose); } static JniMethodInfo vvm; @@ -78,7 +78,7 @@ public virtual IJavaPeerable VirtualObjectMethod () { TypeRef.GetCachedInstanceMethod (ref vom, "VirtualObjectMethod", "()Ljava/lang/Object;"); var lref = JniEnvironment.InstanceMethods.CallObjectMethod (PeerReference, vom); - return JniEnvironment.Runtime.ValueManager.GetObject (ref lref, JniObjectReferenceOptions.CopyAndDispose); + return JniEnvironment.Runtime.ValueManager.GetValue (ref lref, JniObjectReferenceOptions.CopyAndDispose); } static JniMethodInfo fvm; @@ -100,7 +100,7 @@ public IJavaPeerable FinalObjectMethod () { TypeRef.GetCachedInstanceMethod (ref fom, "FinalObjectMethod", "()Ljava/lang/Object;"); var lref = JniEnvironment.InstanceMethods.CallNonvirtualObjectMethod (PeerReference, TypeRef.PeerReference, fom); - return JniEnvironment.Runtime.ValueManager.GetObject (ref lref, JniObjectReferenceOptions.CopyAndDispose); + return JniEnvironment.Runtime.ValueManager.GetValue (ref lref, JniObjectReferenceOptions.CopyAndDispose); } static JniMethodInfo vim1; diff --git a/tests/PerformanceTests/TimingTests.cs b/tests/PerformanceTests/TimingTests.cs index c20f7d2ca41..0402ddbad5d 100644 --- a/tests/PerformanceTests/TimingTests.cs +++ b/tests/PerformanceTests/TimingTests.cs @@ -488,7 +488,7 @@ public void ObjectArrayEnumerationTiming () int len = JniEnvironment.Arrays.GetArrayLength (lrefMethods); for (int i = 0; i < len; ++i) { var v = JniEnvironment.Arrays.GetObjectArrayElement (lrefMethods, i); - methodHandlesGO.Add (vm.ValueManager.GetObject (ref v, JniObjectReferenceOptions.CopyAndDoNotRegister)); + methodHandlesGO.Add (vm.ValueManager.GetValue (ref v, JniObjectReferenceOptions.CopyAndDoNotRegister)); JniObjectReference.Dispose (ref v); } methodsTiming.Stop (); @@ -674,7 +674,7 @@ public unsafe void ObjectCreationTiming () getObjectTime = Stopwatch.StartNew (); for (int i = 0; i < C; ++i) { var h = JniEnvironment.Arrays.GetObjectArrayElement (strings.PeerReference, i); - var o = vm.ValueManager.GetObject (ref h, JniObjectReferenceOptions.CopyAndDispose); + var o = vm.ValueManager.GetValue (ref h, JniObjectReferenceOptions.CopyAndDispose); rlist.Add (o); } getObjectTime.Stop ();