Commit d5dfa0a
authored
[Java.Interop]
Context: dotnet/android#9640
Context: 25de1f3
Context: 3043d89
dotnet/android#9640 completes "unification" with dotnet/java-interop,
updating `Java.Lang.Object` to inherit `Java.Interop.JavaObject`,
just like `src/Java.Base`.
The biggest impediment with this unification step is that the
semantics for referring to a Java instance are quite different:
* .NET for Android née Xamarin.Android née Mono for Android uses
an `(IntPtr, JniHandleOwnership)` pair to specify the JNI object
reference pointer value and what should be done with it.
The *type* of the JNI object reference is *inferred* by the
`JniHandleOwnership` value, e.g.
`JniHandleOwnership.TransferLocalRef` means that the `IntPtr`
value is a JNI local reference.
* dotnet/java-interop uses a
`(ref JniObjectReference, JniObjectReferenceOptions)` pair to
specify the JNI object reference and what can be done with it.
The `JniObjectReference` value contains the type, but -- due
to "pointer trickery"; see 25de1f3 -- it might be *so* invalid
that it cannot be touched at all, and `JniObjectReferenceOptions`
will let us know.
Which brings us to the conundrum: how do we implement the
`Java.Lang.Throwable(IntPtr, JniHandleOwnership)` constructor?
namespace Java.Lang {
public class Throwable {
public Throwable(IntPtr handle, JniHandleOwnership transfer);
}
}
The "obvious and wrong" solution would be to use `ref` with a
temporary…
public Throwable(IntPtr handle, JniHandleOwnership transfer)
: base (ref new JniObjectReference(handle), …)
{
}
…but that won't compile.
Additionally, we want to be able to provide `message` and
`innerException` parameters to `System.Exception`, but the
`JavaException(ref JniObjectReference, JniObjectReferenceOptions)`
constructor requires a valid JNI Object Reference to do that.
If `Java.Lang.Throwable` tries to "work around" this by using the
`JavaException(string, Exception)` constructor:
public Throwable(IntPtr handle, JniHandleOwnership transfer)
: base (_GetMessage (handle), _GetInnerException (handle))
{
…
}
then the `JavaException(string, Exception)` constructor will try to
invoke the `E(String)` constructor on the Java side, which:
1. Is semantically wrong, because we may already have a Java
instance in `handle`, so why are we creating a new instance? But-
2. This fails for constructor subclasses which don't provide a
`E(String)` constructor! Specifically, the
[`JnienvTest.ActivatedDirectThrowableSubclassesShouldBeRegistered`][0]
unit test starts crashing for "bizarre" reasons.
Fix these issues by adding the new `JavaException` constructor:
namespace Java.Interop {
partial class JavaException {
protected JavaException (
ref JniObjectReference reference,
JniObjectReferenceOptions transfer,
JniObjectReference throwableOverride);
}
}
The new `throwableOverride` parameter is used to obtain the `message`
and `innerException` values to provide to the
`System.Exception(string, Exception)` constructor, allowing
`reference` to be a "do not touch" value.
Additionally, add a `protected SetJavaStackTrace()` method which
is used to set the `JavaException.JavaStackTrace` property based on
a `JniObjectReference` value.
Update `Java.Interop.JavaObject` to be `[Serializable]`, and to mark
its fields as *`[NonSerialized]`*. This is needed to fix the
`InstallAndRunTests.JsonDeserializationCreatesJavaHandle()` tests:
E AndroidRuntime: android.runtime.JavaProxyThrowable: [System.Runtime.Serialization.InvalidDataContractException]: AttributedTypesCannotInheritFromNonAttributedSerializableTypes, Java.Lang.Object, Java.Interop.JavaObject
Finally, update
`Java.Interop.JniRuntime.JniValueManager.ConstructPeer()` to *not*
attempt to call `AddPeer()`/etc. when `peer.JniManagedPeerState`
has `JniManagedPeerStates.Activatable` set. Failure to do so was
causing `BindingTests.JavaSideActivation()` to fail, as it was
"losing" a GREF. `JavaSideActivation()` is the scenario described
in 3043d89, in which:
1. The static Java method `CallMethodFromCtor.NewInstance()` creates
an instance of `ConstructorTest`, from Java.
2. The default constructor in the Java Callable Wrapper (JCW) for
`ConstructorTest` chains to the Java `CallMethodFromCtor()` default
constructor.
3. The Java `CallMethodFromCtor()` default constructor invokes the
method `calledFromCtor()`, which is overridden in the C#
`ConstructorTest.CalledFromCtor()` method.
4. This causes the `CallMethodFromCtor.n_CalledFromCtor()` marshal
method to be invoked, which calls
`Java.Lang.Object.GetObject<CallMethodFromCtor>()`, which is akin
to `runtime.ValueManager.GetValue<CallMethodFromCtor>(…)`.
5. `Object.GetObject<CallMethodFromCtor>()` doesn't find an already
existing object mapping for the instance we're still trying to
construct, so it creates a "proxy" object via
[`TypeManager.CreateInstance()`][2], which winds up invoking the
`ConstructorTest(IntPtr, JniHandleOwnership)` "activation"
constructor to create the "proxy". The activation constructor
eventually invokes `JavaObject.Construct()` /
`JniRuntime.JniValueManager.ConstructPeer()`.
This is the first "important" GREF created.
Once the proxy is created, it's state is set to
`JniManagedPeerStates.Activatable`.
6. The `ConstructorTest.CalledFromCtor()` C# override is invoked,
control returns to the Java `CallMethodFromCtor` constructor,
which completes, allowing the JCW `ConstructorTest` constructor
to execute, which calls [`TypeManager.n_Activate(…)`][3], which
is equivalent to `ManagedPeer.Construct()`.
7. `TypeManager.n_Activate(…)` sees that the instance
"needs activation", and invokes the
C# `ConstructorTest()` default constructor. This eventually hits
(again) `JniRuntime.JniValueManager.ConstructPeer()`.
The problem was that on the second
`JniRuntime.JniValueManager.ConstructPeer()` invocation in (7),
another GREF was created, replacing the GREF created in (5), but
without *disposing* of the GREF created in (5). This caused a GREF
leak, which the `JavaSideActivation()` test was looking for.
Updating `JniRuntime.JniValueManager.ConstructPeer()` to bail when
it encounters a Peer with `JniManagedPeerStates.Activatable` avoids
the GREF creation in (7), thus avoiding the GREF leak.
[0]: https://github.com/dotnet/android/blob/f9f421b109b2a0f8f805ed14ab1c8dbe651f563e/tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs#L285-L305
[1]: https://github.com/dotnet/android/blob/main/tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/BindingTests.cs#L97-L115
[2]: https://github.com/dotnet/android/blob/f9f421b109b2a0f8f805ed14ab1c8dbe651f563e/src/Mono.Android/Java.Interop/TypeManager.cs#L240-L332
[3]: https://github.com/dotnet/android/blob/f9f421b109b2a0f8f805ed14ab1c8dbe651f563e/src/Mono.Android/Java.Interop/TypeManager.cs#L132-L167Java.Lang.Object, Mono.Android Unification Changes (#1293)1 parent c86ae26 commit d5dfa0a
File tree
4 files changed
+60
-15
lines changed- src/Java.Interop
- Java.Interop
4 files changed
+60
-15
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
41 | | - | |
| 41 | + | |
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
| |||
55 | 55 | | |
56 | 56 | | |
57 | 57 | | |
58 | | - | |
| 58 | + | |
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
| |||
72 | 72 | | |
73 | 73 | | |
74 | 74 | | |
75 | | - | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
76 | 83 | | |
77 | 84 | | |
78 | 85 | | |
79 | 86 | | |
80 | 87 | | |
81 | 88 | | |
82 | 89 | | |
83 | | - | |
| 90 | + | |
84 | 91 | | |
85 | 92 | | |
86 | 93 | | |
| |||
183 | 190 | | |
184 | 191 | | |
185 | 192 | | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
186 | 200 | | |
187 | 201 | | |
188 | 202 | | |
| |||
193 | 207 | | |
194 | 208 | | |
195 | 209 | | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
196 | 217 | | |
197 | 218 | | |
198 | 219 | | |
199 | 220 | | |
200 | 221 | | |
201 | 222 | | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
202 | 234 | | |
203 | 235 | | |
204 | 236 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
| 10 | + | |
9 | 11 | | |
10 | 12 | | |
11 | 13 | | |
12 | 14 | | |
13 | 15 | | |
14 | 16 | | |
15 | | - | |
16 | | - | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
17 | 23 | | |
18 | 24 | | |
19 | | - | |
| 25 | + | |
20 | 26 | | |
21 | 27 | | |
22 | | - | |
23 | | - | |
| 28 | + | |
| 29 | + | |
24 | 30 | | |
25 | 31 | | |
26 | | - | |
27 | | - | |
| 32 | + | |
| 33 | + | |
28 | 34 | | |
29 | 35 | | |
30 | 36 | | |
| |||
151 | 157 | | |
152 | 158 | | |
153 | 159 | | |
154 | | - | |
| 160 | + | |
155 | 161 | | |
156 | 162 | | |
157 | 163 | | |
158 | 164 | | |
159 | | - | |
| 165 | + | |
160 | 166 | | |
161 | 167 | | |
162 | 168 | | |
| |||
Lines changed: 7 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
100 | | - | |
101 | | - | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
102 | 107 | | |
103 | 108 | | |
104 | 109 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
| 7 | + | |
6 | 8 | | |
7 | 9 | | |
0 commit comments