Commit ae9346a
committed
[Java.Interop] Add
Context: 78d5937
Context: dotnet/android@7a772f0
Context: dotnet/android#9716
Context: dotnet/android@694e975
dotnet/android@7a772f03 added the beginnings of a NativeAOT sample
to dotnet/android which built a ".NET for Android" app using
NativeAOT, which "rhymed with" the `Hello-NativeAOTFromAndroid`
sample in 78d5937.
Further work on the sample showed that it was lacking support for
`Android.App.Application` subclasses. dotnet/android#9716 began
fixing that oversight, but in the process was triggering a stack
overflow because when it needed to create a "proxy" peer around the
`my.MainApplication` Java type, which subclassed
`android.app.Application`, instead of creating an instance of the
expected `MainApplication` C# type, it instead created an instance
of `Android.App.Application`. This was visible from the logs:
Created PeerReference=0x2d06/G IdentityHashCode=0x8edcb07 Instance=0x957d2a Instance.Type=Android.App.Application, Java.Type=my/MainApplication
Note that `Instance.Type` is `Android.App.Application`, not the
in-sample `MainApplication` C# type.
Because the runtime type was `Android.App.Application`, when we later
attempted to dispatch the `Application.OnCreate()` override, this
resulted in a *virtual* invocation of the Java `Application.onCreate()`
method instead of a *non-virtual* invocation of
`Application.onCreate()`. This virtual invocation was the root of a
recursive loop which eventually resulted in a stack overflow.
The fix in dotnet/android@694e975e was to fix
`NativeAotTypeManager.CreatePeer()` so that it properly checked for a
binding of the *runtime type* of the Java instance *before* using the
"fallback" type provided to `Object.GetObject<T>()` in the
`Application.n_OnCreate()` method:
partial class Application {
static void n_OnCreate (IntPtr jnienv, IntPtr native__this)
{
// …
var __this = global::Java.Lang.Object.GetObject<
Android.App.Application // This is the "fallback" NativeAotTypeManager
> (jnienv, native__this, JniHandleOwnership.DoNotTransfer)!;
__this.OnCreate ();
// …
}
}
All well and good.
The problem is that `NativeAotTypeManager` in dotnet/android needs to
support *both* dotnet/java-interop "activation constructors" with a
signature of `(ref JniObjectReference, JniObjectReferenceOptions)`,
*and* the .NET for Android signature of `(IntPtr, JniHandleOwnership)`.
Trying to support both constructors resulted in the need to copy
*all* of `JniRuntime.JniValueManager.CreatePeer()` *and dependencies*,
which felt a bit excessive.
Add a new `JniRuntime.JniValueManager.TryCreatePeer()` method, which
will invoke the activation constructor to create an `IJavaPeerable`:
partial class JniRuntime {
partial class JniValueManager {
protected virtual IJavaPeerable? TryCreatePeer (
ref JniObjectReference reference,
JniObjectReferenceOptions options,
Type targetType);
}
}
If the activation constructor is not found, then `TryCreatePeer()`
shall return `null`, allowing `CreatePeerInstance()` to try for
a base type or, ultimately, the fallback type.
This will allow a future dotnet/android PR to *remove*
`NativeAotTypeManager.CreatePeer()` and its dependencies entirely,
and instead do:
partial class NativeAotTypeManager {
const BindingFlags ActivationConstructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
static readonly Type[] XAConstructorSignature = new Type [] { typeof (IntPtr), typeof (JniHandleOwnership) };
protected override IJavaPeerable TryCreatePeer (ref JniObjectReference reference, JniObjectReferenceOptions options, Type type)
{
var c = type.GetConstructor (ActivationConstructorBindingFlags, null, XAConstructorSignature, null);
if (c != null) {
var args = new object[] {
reference.Handle,
JniHandleOwnership.DoNotTransfer,
};
var p = (IJavaPeerable) c.Invoke (args);
JniObjectReference.Dispose (ref reference, options);
return p;
}
return base.TryCreatePeer (ref reference, options, type);
}
}
vastly reducing the code it needs to care about.JniRuntime.JniValueManager.TryCreatePeer()
1 parent e288589 commit ae9346a
File tree
2 files changed
+31
-30
lines changed- src/Java.Interop
- Java.Interop
2 files changed
+31
-30
lines changedLines changed: 30 additions & 30 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
336 | 336 | | |
337 | 337 | | |
338 | 338 | | |
339 | | - | |
340 | | - | |
| 339 | + | |
| 340 | + | |
341 | 341 | | |
342 | 342 | | |
343 | 343 | | |
344 | | - | |
345 | | - | |
346 | | - | |
347 | | - | |
348 | | - | |
349 | | - | |
350 | | - | |
351 | | - | |
352 | | - | |
353 | | - | |
354 | | - | |
355 | | - | |
| 344 | + | |
| 345 | + | |
356 | 346 | | |
357 | 347 | | |
358 | | - | |
359 | | - | |
360 | | - | |
| 348 | + | |
361 | 349 | | |
362 | 350 | | |
363 | | - | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
364 | 354 | | |
365 | 355 | | |
366 | 356 | | |
| |||
373 | 363 | | |
374 | 364 | | |
375 | 365 | | |
376 | | - | |
| 366 | + | |
| 367 | + | |
377 | 368 | | |
378 | | - | |
| 369 | + | |
379 | 370 | | |
380 | | - | |
| 371 | + | |
381 | 372 | | |
382 | 373 | | |
383 | 374 | | |
| |||
391 | 382 | | |
392 | 383 | | |
393 | 384 | | |
394 | | - | |
| 385 | + | |
395 | 386 | | |
396 | 387 | | |
397 | | - | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
398 | 396 | | |
399 | 397 | | |
400 | 398 | | |
401 | | - | |
402 | | - | |
403 | | - | |
404 | | - | |
405 | | - | |
406 | | - | |
407 | | - | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
408 | 408 | | |
409 | 409 | | |
410 | 410 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
| |||
0 commit comments