Skip to content

Commit bf02660

Browse files
committed

File tree

6 files changed

+131
-24
lines changed

6 files changed

+131
-24
lines changed

src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -401,15 +401,36 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type)
401401
Type type)
402402
{
403403
type = Runtime.TypeManager.GetInvokerType (type) ?? type;
404-
return TryCreatePeer (ref reference, options, type);
404+
405+
var self = GetUninitializedObject (type);
406+
var constructed = false;
407+
try {
408+
constructed = TryConstructPeer (self, ref reference, options, type);
409+
} finally {
410+
if (!constructed) {
411+
GC.SuppressFinalize (self);
412+
self = null;
413+
}
414+
}
415+
return self;
416+
417+
static IJavaPeerable GetUninitializedObject (
418+
[DynamicallyAccessedMembers (Constructors)]
419+
Type type)
420+
{
421+
var v = (IJavaPeerable) System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject (type);
422+
v.SetJniManagedPeerState (JniManagedPeerStates.Replaceable | JniManagedPeerStates.Activatable);
423+
return v;
424+
}
405425
}
406426

407427
const BindingFlags ActivationConstructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
408428
static readonly Type ByRefJniObjectReference = typeof (JniObjectReference).MakeByRefType ();
409429
static readonly Type[] JIConstructorSignature = new Type [] { ByRefJniObjectReference, typeof (JniObjectReferenceOptions) };
410430

411431

412-
protected virtual IJavaPeerable? TryCreatePeer (
432+
protected virtual bool TryConstructPeer (
433+
IJavaPeerable self,
413434
ref JniObjectReference reference,
414435
JniObjectReferenceOptions options,
415436
[DynamicallyAccessedMembers (Constructors)]
@@ -421,11 +442,11 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type)
421442
reference,
422443
options,
423444
};
424-
var p = (IJavaPeerable) c.Invoke (args);
445+
c.Invoke (self, args);
425446
reference = (JniObjectReference) args [0];
426-
return p;
447+
return true;
427448
}
428-
return null;
449+
return false;
429450
}
430451

431452
public object? CreateValue (

src/Java.Interop/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ static Java.Interop.JniEnvironment.EndMarshalMethod(ref Java.Interop.JniTransiti
44
virtual Java.Interop.JniRuntime.OnEnterMarshalMethod() -> void
55
virtual Java.Interop.JniRuntime.OnUserUnhandledException(ref Java.Interop.JniTransition transition, System.Exception! e) -> void
66
virtual Java.Interop.JniRuntime.JniTypeManager.GetInvokerTypeCore(System.Type! type) -> System.Type?
7-
virtual Java.Interop.JniRuntime.JniValueManager.TryCreatePeer(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type! type) -> Java.Interop.IJavaPeerable?
7+
virtual Java.Interop.JniRuntime.JniValueManager.TryConstructPeer(Java.Interop.IJavaPeerable! self, ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type! type) -> bool
88
Java.Interop.JavaException.JavaException(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions transfer, Java.Interop.JniObjectReference throwableOverride) -> void
99
Java.Interop.JavaException.SetJavaStackTrace(Java.Interop.JniObjectReference peerReferenceOverride = default(Java.Interop.JniObjectReference)) -> void
1010
Java.Interop.JniRuntime.JniTypeManager.GetInvokerType(System.Type! type) -> System.Type?

src/Java.Runtime.Environment/Java.Interop/ManagedValueManager.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,6 @@ public override void AddPeer (IJavaPeerable value)
5757
var r = value.PeerReference;
5858
if (!r.IsValid)
5959
throw new ObjectDisposedException (value.GetType ().FullName);
60-
var o = PeekPeer (value.PeerReference);
61-
if (o != null)
62-
return;
6360

6461
if (r.Type != JniObjectReferenceType.Global) {
6562
value.SetPeerReference (r.NewGlobalRef ());
@@ -80,7 +77,7 @@ public override void AddPeer (IJavaPeerable value)
8077
var p = peers [i];
8178
if (!JniEnvironment.Types.IsSameObject (p.PeerReference, value.PeerReference))
8279
continue;
83-
if (Replaceable (p)) {
80+
if (Replaceable (p, value)) {
8481
peers [i] = value;
8582
} else {
8683
WarnNotReplacing (key, value, p);
@@ -91,11 +88,12 @@ public override void AddPeer (IJavaPeerable value)
9188
}
9289
}
9390

94-
static bool Replaceable (IJavaPeerable peer)
91+
static bool Replaceable (IJavaPeerable peer, IJavaPeerable value)
9592
{
9693
if (peer == null)
9794
return true;
98-
return (peer.JniManagedPeerState & JniManagedPeerStates.Replaceable) == JniManagedPeerStates.Replaceable;
95+
return peer.JniManagedPeerState.HasFlag (JniManagedPeerStates.Replaceable) &&
96+
!value.JniManagedPeerState.HasFlag (JniManagedPeerStates.Replaceable);
9997
}
10098

10199
void WarnNotReplacing (int key, IJavaPeerable ignoreValue, IJavaPeerable keepValue)

tests/Java.Interop-Tests/Java.Interop/CallVirtualFromConstructorBase.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Runtime.CompilerServices;
23

34
using Java.Interop;
45
using Java.Interop.GenericMarshaler;
@@ -16,14 +17,31 @@ public override JniPeerMembers JniPeerMembers {
1617
}
1718

1819
public unsafe CallVirtualFromConstructorBase (int value)
20+
: this (value, useNewObject: false)
21+
{
22+
}
23+
24+
public unsafe CallVirtualFromConstructorBase (int value, bool useNewObject)
1925
: this (ref *InvalidJniObjectReference, JniObjectReferenceOptions.None)
2026
{
2127
if (PeerReference.IsValid)
2228
return;
2329

24-
var peer = JniPeerMembers.InstanceMethods.StartGenericCreateInstance ("(I)V", GetType (), value);
30+
const string __id = "(I)V";
31+
32+
if (useNewObject) {
33+
var ctors = JniPeerMembers.InstanceMethods.GetConstructorsForType (GetType ());
34+
var init = ctors.GetConstructor (__id);
35+
36+
JniArgumentValue* __args = stackalloc JniArgumentValue [1];
37+
__args [0] = new JniArgumentValue (value);
38+
var lref = JniEnvironment.Object.NewObject (ctors.JniPeerType.PeerReference, init, __args);
39+
Construct (ref lref, JniObjectReferenceOptions.CopyAndDispose);
40+
return;
41+
}
42+
var peer = JniPeerMembers.InstanceMethods.StartGenericCreateInstance (__id, GetType (), value);
2543
Construct (ref peer, JniObjectReferenceOptions.CopyAndDispose);
26-
JniPeerMembers.InstanceMethods.FinishGenericCreateInstance ("(I)V", this, value);
44+
JniPeerMembers.InstanceMethods.FinishGenericCreateInstance (__id, this, value);
2745
}
2846

2947
public CallVirtualFromConstructorBase (ref JniObjectReference reference, JniObjectReferenceOptions options)

tests/Java.Interop-Tests/Java.Interop/CallVirtualFromConstructorDerived.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,22 @@ public override JniPeerMembers JniPeerMembers {
2727
public bool InvokedConstructor;
2828

2929
public CallVirtualFromConstructorDerived (int value)
30-
: base (value)
30+
: this (value, useNewObject: false)
31+
{
32+
}
33+
34+
public CallVirtualFromConstructorDerived (int value, bool useNewObject)
35+
: base (value, useNewObject)
3136
{
3237
InvokedConstructor = true;
33-
if (value != calledValue)
38+
39+
if (useNewObject && calledValue != 0) {
40+
// calledValue was set on a *different* instance! So it's 0 here.
41+
throw new ArgumentException (
42+
string.Format ("value '{0}' doesn't match expected value '{1}'.", value, 0),
43+
"value");
44+
}
45+
if (!useNewObject && value != calledValue)
3446
throw new ArgumentException (
3547
string.Format ("value '{0}' doesn't match expected value '{1}'.", value, calledValue),
3648
"value");

tests/Java.Interop-Tests/Java.Interop/InvokeVirtualFromConstructorTests.cs

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace Java.InteropTests {
1515
public class InvokeVirtualFromConstructorTests : JavaVMFixture
1616
{
1717
[Test]
18-
public void CreateManagedInstanceFirst ()
18+
public void CreateManagedInstanceFirst_WithAllocObject ()
1919
{
2020
CallVirtualFromConstructorDerived.Intermediate_FromActivationConstructor = null;
2121
CallVirtualFromConstructorDerived.Intermediate_FromCalledFromConstructor = null;
@@ -39,17 +39,75 @@ public void CreateManagedInstanceFirst ()
3939
"Expected t and registered to be the same instance; " +
4040
$"t={RuntimeHelpers.GetHashCode (t).ToString ("x")}, " +
4141
$"registered={RuntimeHelpers.GetHashCode (registered).ToString ("x")}");
42-
Assert.AreNotSame (t, acIntermediate,
43-
"Expected t and registered to be the same instance; " +
44-
$"t={RuntimeHelpers.GetHashCode (t).ToString ("x")}, " +
45-
$"registered={RuntimeHelpers.GetHashCode (registered).ToString ("x")}");
42+
Assert.IsNull (acIntermediate,
43+
"Activation Constructor should not have been called, because of AllocObject semantics");
4644
Assert.AreSame (t, cfIntermediate,
4745
"Expected t and cfIntermediate to be the same instance; " +
4846
$"t={RuntimeHelpers.GetHashCode (t).ToString ("x")}, " +
4947
$"cfIntermediate={RuntimeHelpers.GetHashCode (cfIntermediate).ToString ("x")}");
5048

49+
Dispose (ref CallVirtualFromConstructorDerived.Intermediate_FromActivationConstructor);
50+
Dispose (ref CallVirtualFromConstructorDerived.Intermediate_FromCalledFromConstructor);
51+
}
52+
53+
static void Dispose<T> (ref T peer)
54+
where T : class, IJavaPeerable
55+
{
56+
if (peer == null)
57+
return;
58+
59+
peer.Dispose ();
60+
peer = null;
61+
}
62+
63+
[Test]
64+
public void CreateManagedInstanceFirst_WithNewObject ()
65+
{
5166
CallVirtualFromConstructorDerived.Intermediate_FromActivationConstructor = null;
5267
CallVirtualFromConstructorDerived.Intermediate_FromCalledFromConstructor = null;
68+
69+
using var t = new CallVirtualFromConstructorDerived (42, useNewObject: true);
70+
Assert.IsFalse (
71+
t.Called,
72+
"CalledFromConstructor method override was called on a different instance.");
73+
Assert.IsFalse (
74+
t.InvokedActivationConstructor,
75+
"Activation Constructor should not have been called, as calledFromConstructor() is invoked before ManagedPeer.construct().");
76+
Assert.IsTrue (
77+
t.InvokedConstructor,
78+
"(int) constructor should have been called, via ManagedPeer.construct().");
79+
80+
var registered = JniRuntime.CurrentRuntime.ValueManager.PeekValue (t.PeerReference);
81+
var acIntermediate = CallVirtualFromConstructorDerived.Intermediate_FromActivationConstructor;
82+
var cfIntermediate = CallVirtualFromConstructorDerived.Intermediate_FromCalledFromConstructor;
83+
84+
Assert.AreSame (t, registered,
85+
"Expected t and registered to be the same instance; " +
86+
$"t={RuntimeHelpers.GetHashCode (t).ToString ("x")}, " +
87+
$"registered={RuntimeHelpers.GetHashCode (registered).ToString ("x")}");
88+
Assert.IsNotNull (acIntermediate,
89+
"Activation Constructor should have been called, because of NewObject");
90+
Assert.IsTrue (
91+
acIntermediate.Called,
92+
"CalledFromConstructor method override should have been called on acIntermediate.");
93+
Assert.IsTrue (
94+
acIntermediate.InvokedActivationConstructor,
95+
"Activation Constructor should have been called on intermediate instance, as calledFromConstructor() is invoked before ManagedPeer.construct().");
96+
Assert.AreNotSame (t, acIntermediate,
97+
"Expected t and registered to be different instances; " +
98+
$"t={RuntimeHelpers.GetHashCode (t).ToString ("x")}, " +
99+
$"acIntermediate={RuntimeHelpers.GetHashCode (acIntermediate).ToString ("x")}");
100+
Assert.AreNotSame (t, cfIntermediate,
101+
"Expected t and cfIntermediate to be different instances; " +
102+
$"t={RuntimeHelpers.GetHashCode (t).ToString ("x")}, " +
103+
$"cfIntermediate={RuntimeHelpers.GetHashCode (cfIntermediate).ToString ("x")}");
104+
Assert.AreSame (acIntermediate, cfIntermediate,
105+
"Expected acIntermediate and cfIntermediate to be the same instance; " +
106+
$"acIntermediate={RuntimeHelpers.GetHashCode (acIntermediate).ToString ("x")}, " +
107+
$"cfIntermediate={RuntimeHelpers.GetHashCode (cfIntermediate).ToString ("x")}");
108+
109+
Dispose (ref CallVirtualFromConstructorDerived.Intermediate_FromActivationConstructor);
110+
Dispose (ref CallVirtualFromConstructorDerived.Intermediate_FromCalledFromConstructor);
53111
}
54112

55113
[Test]
@@ -81,14 +139,14 @@ public void CreateJavaInstanceFirst ()
81139
Assert.AreSame (t, acIntermediate,
82140
"Expected t and registered to be the same instance; " +
83141
$"t={RuntimeHelpers.GetHashCode (t).ToString ("x")}, " +
84-
$"registered={RuntimeHelpers.GetHashCode (registered).ToString ("x")}");
142+
$"acIntermediate={RuntimeHelpers.GetHashCode (acIntermediate).ToString ("x")}");
85143
Assert.AreSame (t, cfIntermediate,
86144
"Expected t and cfIntermediate to be the same instance; " +
87145
$"t={RuntimeHelpers.GetHashCode (t).ToString ("x")}, " +
88146
$"cfIntermediate={RuntimeHelpers.GetHashCode (cfIntermediate).ToString ("x")}");
89147

90-
CallVirtualFromConstructorDerived.Intermediate_FromActivationConstructor = null;
91-
CallVirtualFromConstructorDerived.Intermediate_FromCalledFromConstructor = null;
148+
Dispose (ref CallVirtualFromConstructorDerived.Intermediate_FromActivationConstructor);
149+
Dispose (ref CallVirtualFromConstructorDerived.Intermediate_FromCalledFromConstructor);
92150
}
93151
}
94152
}

0 commit comments

Comments
 (0)