Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Java.Interop] Add
JniRuntime.JniValueManager.TryCreatePeer()
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.
- Loading branch information