Commit 3043d89
committed
[Java.Interop] Add JniManagedPeerStates, IJavaPeerable.JniManagedPeerState
We want to retrofit Xamarin.Android to use Java.Interop. This in turn
requires that Java.Interop be able to do everything that
Xamarin.Android does, with the added complication that Java.Interop
will be used in Xamarin.Android before major parts are complete (e.g.
Activation [8c83f64] and Issue #11 [0]).
Fortunately we can look at how Xamarin.Android is architected to see
what we might need to provide in advance before it's actually used,
which leads us to Xamarin.Android's internal
Java.Interop.IJavaObjectEx interface:
interface IJavaObjectEx {
IntPtr KeyHandle {get; set;}
bool IsProxy {get; set;}
bool NeedsActivation {get; set;}
IntPtr ToLocalJniHandle ();
}
Of those members, IJavaObjectEx.KeyHandle is already exposed as
IJavaPeerable.JniIdentityHashCode, and
IJavaObjectEx.ToLocalJniHandle() shouldn't be needed (it was added to
incorrectly address a multithreading-related bug).
That leaves IJavaObjectEx.IsProxy and IJavaObjectEx.NeedsActivation,
which are both involved in Java-side activation.
In the interest of avoiding API breaks in the future, we need to
support those constructs in Java.Interop *now*, even if they won't be
fully utilized until later.
Additionally, those names suck for "public" names -- what do they
*mean*, sans context? -- and, while things have been reasonably stable
here for the past several years, I'm not entirely certain that more
such states won't need to be added in the future, so we need to
support IJavaObjectEx.IsProxy and IJavaObjectEx.NeedsActivation
semantics in an "extensible" manner?
The solution? Yet another [Flags] enum!
[Flags]
public enum JniManagedPeerStates {
None,
Activatable = (1 << 0),
Replaceable = (1 << 1),
}
The use of a [Flags] enum allows us to add additional states in the
future, should we need to do so.
JniManagedPeerStates.Activatable is IJavaObjectEx.NeedsActivation, and
means that IJavaPeerable.PeerReference was set *before* the
constructor was invoked. (Setting IJavaPeerable.PeerReference before
the constructor executes is not yet done in Java.Interop.)
It means that a future "proper" constructor invocation is assumed to
be forthcoming, as during Java activation, IF the Java constructor
virtually invokes a method which is overridden in managed code, a
managed peer will need to be constructed so that the method override
can be invoked, and "later" the "real" constructor will be invoked.
https://developer.xamarin.com/guides/android/under_the_hood/architecture/#Java_Activation
1. `new Peer(...)` is invoked from Java.
2. A super class constructor of NativePeer virtually invokes a method
overridden by Peer in managed code.
3. The Marshal Method is executed, which needs to delegate the method
to *something*, and thus creates a new managed Peer instance
through the activation constructor.
This created managed peer instance will have the
JniManagedPeerStates.Activatable state set.
4. The managed override is executed and returns back to Java.
5. Once all super constructors have finished, the Peer constructor
executes com.xamarin.java_interop.ManagedPeer.runConstructor(),
6. ManagedPeer.runConstructor() invokes the appropriate corresponding
constructor on the instance created in (3). If the
JniManagedPeerStates.Activatable state *isn't* set, then the
ManagedPeer.runConstructor() call would be *ignored*.
This also means that *two constructors* will be invoked on *one
instrance*.
The JniManagedPeerStates.Activatable state needs to be set to sanely
prevent invoking constructors more than is intended on a given
peer instance.
JniManagedPeerStates.Replaceable is IJavaObjectEx.IsProxy, and means
that the Peer instance was created through the activation constructor.
It additionally means that if two managed instances are created around
the same Java instance, the non-Replaceable instance will be the one
returned by JniRuntime.JniValueManager.PeekObject().
Normally, JniManagedPeerStates.Replaceable shouldn't be needed, but
there is one environment where it is:
Android devices which are pre-Honeycomb (API-11). On those devices,
JNIEnv.AllocObject() cannot be used (8c4248b), so something very
similar but not quite like the above Activation case can happen when
constructing instances *from managed code*.
In "normal" use -- JNIEnv::AllocObject() works! -- managed
construction is:
1. `new Peer()` invokes managed constructor.
2. Managed constructor calls
JniPeerMembers.InstanceMethods.StartCreateInstance(), which uses
JNIEnv::AllocObject() to create a Java instance *without executing
the Java instance constructor*.
3. JniRuntime.JniValueManager.Construct() adds the Java instance from
(2) to the mapping table.
4. The constructor calls
JniPeerMembers.InstanceMethods.FinishCreateInstance(), which
invokes the Java instance constructor, and if a Java instance
constructor virtually invokes a method overridden in managed code,
the marshal method will find the instance created in (1).
When JNIEnv::AllocObject() doesn't work, the above falls down:
1. `new Peer()` invokes managed constructor.
2. Managed constructor calls
JniPeerMembers.InstanceMethods.StartCreateInstance(), which uses
JNIEnv::NewObject() to create a Java instance *and executes
the Java instance constructor*.
3. If a Java constructor virtually invokes a method overridden in
managed code, the marshal method will be invoked and won't find an
already-existing peer for the Java instance, and thus will create
one via JniRuntime.JniValueManager.CreatePeer().
4. JniRuntime.JniValueManager.CreatePeer() will set the
JniManagedPeerStates.Replaceable state on the instance created in
(3).
Note: at this point there are *two* managed peers which want to
"own" the Java instance: the instance created in (1) which is
still being constructed (!), and the instance created in (3).
5. The overriding managed method returns control to the Java
constructor, which eventually completes execution and returns
execution to the Peer managed constructor in (1).
6. The Peer constructor invokes
JniRuntime.JniValueManager.Construct() to add the instance from
(1) to the instance mapping table, a mapping which *already
exists* because of (3).
There needs to be a way for (6) to replace the mapping created in (3)
with the Peer instance in (1), and JniManagedPeerStates.Replaceable is
how that is tracked.
(Aside: supporting platforms that have broken JNIEnv::AllocObject()
implementations is a GIANT PAIN IN THE ASS.)
To use JniManagedPeerStates, update IJavaPeerable to add the following
members:
partial interface IJavaPeerable {
JniManagedPeerStates JniManagedPeerState {get;}
void SetJniManagedPeerState (JniManagedPeerStates value);
}
IJavaPeerable.JniManagedPeerState is the current state of the managed
peer. IJavaPeerable.SetJniManagedPeerState() allows updating the
current state of the managed peer, permitting the state to be tracked
cross activation constructor calls.
[0]: dotnet/java-interop#111 parent 51f7862 commit 3043d89
File tree
10 files changed
+129
-2
lines changed- src
- Java.Interop
- Documentation/Java.Interop
- Java.Interop
- Java.Runtime.Environment/Java.Interop
10 files changed
+129
-2
lines changedLines changed: 27 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
195 | 195 | | |
196 | 196 | | |
197 | 197 | | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
198 | 211 | | |
199 | 212 | | |
200 | 213 | | |
| |||
285 | 298 | | |
286 | 299 | | |
287 | 300 | | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
288 | 315 | | |
289 | 316 | | |
290 | 317 | | |
| |||
Lines changed: 34 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
76 | 76 | | |
77 | 77 | | |
78 | 78 | | |
| 79 | + | |
79 | 80 | | |
80 | 81 | | |
81 | 82 | | |
| |||
184 | 185 | | |
185 | 186 | | |
186 | 187 | | |
| 188 | + | |
187 | 189 | | |
188 | 190 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
22 | 28 | | |
23 | 29 | | |
24 | 30 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
| 15 | + | |
14 | 16 | | |
15 | 17 | | |
16 | 18 | | |
| |||
232 | 234 | | |
233 | 235 | | |
234 | 236 | | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
235 | 241 | | |
236 | 242 | | |
237 | 243 | | |
| |||
247 | 253 | | |
248 | 254 | | |
249 | 255 | | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
250 | 261 | | |
251 | 262 | | |
252 | 263 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
| 13 | + | |
12 | 14 | | |
13 | 15 | | |
14 | 16 | | |
| |||
44 | 46 | | |
45 | 47 | | |
46 | 48 | | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
47 | 53 | | |
48 | 54 | | |
49 | 55 | | |
| |||
136 | 142 | | |
137 | 143 | | |
138 | 144 | | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
139 | 149 | | |
140 | 150 | | |
141 | 151 | | |
| |||
151 | 161 | | |
152 | 162 | | |
153 | 163 | | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
154 | 169 | | |
155 | 170 | | |
156 | 171 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
Lines changed: 11 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
68 | 76 | | |
69 | 77 | | |
70 | 78 | | |
| |||
229 | 237 | | |
230 | 238 | | |
231 | 239 | | |
232 | | - | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
233 | 243 | | |
234 | 244 | | |
235 | 245 | | |
| |||
Lines changed: 8 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
112 | | - | |
| 112 | + | |
113 | 113 | | |
114 | 114 | | |
115 | 115 | | |
| |||
127 | 127 | | |
128 | 128 | | |
129 | 129 | | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
130 | 137 | | |
131 | 138 | | |
132 | 139 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
| 112 | + | |
112 | 113 | | |
113 | 114 | | |
114 | 115 | | |
| |||
0 commit comments