You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Context: 78d5937
Context: be6cc8f
Context: dotnet/android@7a772f0
Context: dotnet/android#9716
Context: dotnet/android@694e975dotnet/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:
[Application (Name = "my.MainApplication")]
public class MainApplication : Application
{
public MainApplication (IntPtr handle, JniHandleOwnership transfer)
: base (handle, transfer)
{
}
public override void OnCreate ()
{
base.OnCreate ();
}
}
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 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.
Additionally, add `JniRuntime.JniTypeManager.GetInvokerType()`:
partial class JniRuntime {
partial class JniTypeManager {
public Type? GetInvokerType (Type type);
protected virtual Type? GetInvokerTypeCore (Type type);
}
}
`GetInvokerType()` calls `GetInvokerTypeCore()`, allowing flexibility
for subclasses to lookup the "string-based" the .NET for Android -style
Invoker types that JavaInterop1-style bindings used before commit
be6cc8f.
0 commit comments