Skip to content

[CoreCLR] Override GetInvokerTypeCore in AndroidTypeManager #9978

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 27, 2025

Conversation

simonrozsival
Copy link
Member

This is a follow-up to #9973
Replaces #9977 (CI was failing due to long branch name)

This PR fixes the following crash at the startup of dotnet new maui -sc when the AndroidTypeManager is used:

--------- beginning of crash
03-27 09:57:38.276 25497 25497 E AndroidRuntime: FATAL EXCEPTION: main
03-27 09:57:38.276 25497 25497 E AndroidRuntime: Process: com.companyname.testmaui, PID: 25497
03-27 09:57:38.276 25497 25497 E AndroidRuntime: android.runtime.JavaProxyThrowable: [System.MemberAccessException]: Acc_CreateAbstEx, Android.Views.LayoutInflater
03-27 09:57:38.276 25497 25497 E AndroidRuntime: at System.Reflection.RuntimeConstructorInfo.CheckCanCreateInstance + 0x3c(Unknown Source)
03-27 09:57:38.276 25497 25497 E AndroidRuntime: at System.Reflection.RuntimeConstructorInfo.ThrowNoInvokeException + 0x0(Unknown Source)
03-27 09:57:38.276 25497 25497 E AndroidRuntime: at System.Reflection.RuntimeConstructorInfo.Invoke + 0xe(Unknown Source)
03-27 09:57:38.276 25497 25497 E AndroidRuntime: at System.Reflection.ConstructorInfo.Invoke + 0x0(Unknown Source)
03-27 09:57:38.276 25497 25497 E AndroidRuntime: at Microsoft.Android.Runtime.ManagedValueManager.TryCreatePeer + 0x3b(Unknown Source)
03-27 09:57:38.276 25497 25497 E AndroidRuntime: at Java.Interop.JniRuntime+JniValueManager.TryCreatePeerInstance + 0x19(Unknown Source)
03-27 09:57:38.276 25497 25497 E AndroidRuntime: at Java.Interop.JniRuntime+JniValueManager.CreatePeerInstance + 0x5a(Unknown Source)
03-27 09:57:38.276 25497 25497 E AndroidRuntime: at Java.Interop.JniRuntime+JniValueManager.CreatePeer + 0x150(Unknown Source)
03-27 09:57:38.276 25497 25497 E AndroidRuntime: at Java.Interop.JniRuntime+JniValueManager.GetPeer + 0x169(Unknown Source)
03-27 09:57:38.276 25497 25497 E AndroidRuntime: at Java.Lang.Object.GetObject + 0x1d(Unknown Source)
03-27 09:57:38.276 25497 25497 E AndroidRuntime: at Java.Lang.Object._GetObject + 0x19(Unknown Source)
03-27 09:57:38.276 25497 25497 E AndroidRuntime: at Java.Lang.Object.GetObject + 0x1(Unknown Source)
03-27 09:57:38.276 25497 25497 E AndroidRuntime: at Android.Views.LayoutInflater.From + 0x3f(Unknown Source)
03-27 09:57:38.276 25497 25497 E AndroidRuntime: at Microsoft.Maui.Platform.MauiContextExtensions.GetLayoutInflater + 0x2b(Unknown Source)
...

AndroidTypeManager didn't override the GetInvokerTypeCore method and the base implementation of this method in JniTypeManager only looks at JniTypeSignatureAttribute to find the invoker type. LayoutInflater doesn't have this attribute and it relies on the logic in JavaObjectExtensions.GetInvokerType. This method will look for the invoker based on a naming convention by adding the Invoker suffix to the original type name.

This PR adds an override of GetInvokerTypeCore which calls JavaObjectExtensions.GetInvokerType when needed to keep backwards compatibility.

@jonpryor
Copy link
Contributor

Oddly, the macOS build failed in create-installers:

         /Users/builder/azdo/_work/4/s/xamarin-android/bin/Release/dotnet/sdk/10.0.100-preview.4.25175.6/Sdks/NuGet.Build.Tasks.Pack/build/NuGet.Build.Tasks.Pack.targets(221,5): error NU5123: Warning As Error: The file 'package/services/metadata/core-properties/69a20893954841c7bf2bc63c46c34b0a.psmdcp' path, name, or both are too long. Your package might not work without long file path support. Please shorten the file path or file name. [/Users/builder/azdo/_work/4/s/xamarin-android/build-tools/create-packs/Microsoft.Android.Runtime.proj] [/Users/builder/azdo/_work/4/s/xamarin-android/build-tools/create-packs/Microsoft.Android.Sdk.proj]
         /Users/builder/azdo/_work/4/s/xamarin-android/build-tools/create-packs/Directory.Build.targets(78,5): error MSB3073: The command ""/Users/builder/azdo/_work/4/s/xamarin-android/bin/Release/dotnet/dotnet" pack -p:Configuration=Release -p:IncludeSymbols=False -p:AndroidRID=android-arm -p:AndroidRuntime=NativeAOT "/Users/builder/azdo/_work/4/s/xamarin-android/build-tools/create-packs/Microsoft.Android.Runtime.proj"" exited with code 1. [/Users/builder/azdo/_work/4/s/xamarin-android/build-tools/create-packs/Microsoft.Android.Sdk.proj]

No idea what's up with that. I've restarted that build.

@jonathanpeppers
Copy link
Member

@jonpryor I think the other PR triggered a build for the same commit hash, this one is:

@jonpryor jonpryor merged commit 4f10e13 into main Mar 27, 2025
54 of 59 checks passed
@jonpryor jonpryor deleted the dev/simonrozsival/fix-get-invoker-type branch March 27, 2025 23:06
jonathanpeppers pushed a commit that referenced this pull request Mar 28, 2025
…9978)

Context: e347cd3
Context: #9962
Context: dotnet/java-interop@dd3c1d0

CoreCLR + `dotnet new maui -sc` (#9962) crashes:

	E AndroidRuntime: FATAL EXCEPTION: main
	E AndroidRuntime: Process: com.companyname.testmaui, PID: 25497
	E AndroidRuntime: android.runtime.JavaProxyThrowable: [System.≈]: Acc_CreateAbstEx, Android.Views.LayoutInflater
	E AndroidRuntime: at System.Reflection.RuntimeConstructorInfo.CheckCanCreateInstance + 0x3c(Unknown Source)
	E AndroidRuntime: at System.Reflection.RuntimeConstructorInfo.ThrowNoInvokeException + 0x0(Unknown Source)
	E AndroidRuntime: at System.Reflection.RuntimeConstructorInfo.Invoke + 0xe(Unknown Source)
	E AndroidRuntime: at System.Reflection.ConstructorInfo.Invoke + 0x0(Unknown Source)
	E AndroidRuntime: at Microsoft.Android.Runtime.ManagedValueManager.TryCreatePeer + 0x3b(Unknown Source)
	E AndroidRuntime: at Java.Interop.JniRuntime+JniValueManager.TryCreatePeerInstance + 0x19(Unknown Source)
	E AndroidRuntime: at Java.Interop.JniRuntime+JniValueManager.CreatePeerInstance + 0x5a(Unknown Source)
	E AndroidRuntime: at Java.Interop.JniRuntime+JniValueManager.CreatePeer + 0x150(Unknown Source)
	E AndroidRuntime: at Java.Interop.JniRuntime+JniValueManager.GetPeer + 0x169(Unknown Source)
	E AndroidRuntime: at Java.Lang.Object.GetObject + 0x1d(Unknown Source)
	E AndroidRuntime: at Java.Lang.Object._GetObject + 0x19(Unknown Source)
	E AndroidRuntime: at Java.Lang.Object.GetObject + 0x1(Unknown Source)
	E AndroidRuntime: at Android.Views.LayoutInflater.From + 0x3f(Unknown Source)
	E AndroidRuntime: at Microsoft.Maui.Platform.MauiContextExtensions.GetLayoutInflater + 0x2b(Unknown Source)

`AndroidTypeManager` didn't override the
`JniRuntime.JniTypeManager.GetInvokerTypeCore()` method
(dotnet/java-interop@dd3c1d05) and the base implementation of this
method in `JniTypeManager` only looks at `JniTypeSignatureAttribute`
to find the "invoker" type.

.NET for Android bindings don't use `JniTypeSignatureAttribute`, they
use `RegisterAttribute`; for example, consider the abstract
`Android.Views.LayoutInflater` class:

	[Register ("android/view/LayoutInflater", DoNotGenerateAcw=true)]
	partial class LayoutInflater : Java.Lang.Object {
	}

	[global::Android.Runtime.Register ("android/view/LayoutInflater", DoNotGenerateAcw=true)]
	internal partial class LayoutInflaterInvoker : LayoutInflater {
	}

The default `JniRuntime.JniTypeManager.GetInvokerType()` behavior
will not work with .NET for Android bindings.

This has historically been fine because
`AndroidValueManager.CreatePeer()` / `TypeManager.CreateInstance()`
would use `JavaObjectExtensions.GetInvokerType()` to obtain the
`Invoker` type via "pattern-based Reflection", appending `Invoker`
to the abstract type name to obtain a non-abstract constructable type.

That historical behavior changed with e347cd3: CoreCLR doesn't use
`AndroidValueManager`, but instead now uses `ManagedValueManager`,
which *doesn't* call `TypeManager.CreateInstance()` or
`JavaObjectExtensions.GetInvokerType()`.  It instead relies on
`JniRuntime.JniTypeManager.GetInvokerType()`, and as that was not
overridden by `AndroidTypeManager`, the invoker for `LayoutInflater`
could not be found, resulting in a `MemberAccessException` as we
attempt to invoke the constructor on the *abstract* `LayoutInflater`
type itself! [^0].

Add an `AndroidTypeManager.GetInvokerTypeCore()` method override
which calls `JavaObjectExtensions.GetInvokerType` when needed to keep
backwards compatibility.

[^0]:   The "complete" exception message would be:  
        Cannot create an instance of Android.Views.LayoutInflater because it is an abstract class.
grendello added a commit that referenced this pull request Mar 28, 2025
* main:
  Bump to dotnet/java-interop@7b45b166 (#9982)
  [Mono.Android] Add AndroidTypeManager.GetInvokerTypeCore() override (#9978)
@github-actions github-actions bot locked and limited conversation to collaborators Apr 27, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants