Skip to content

Starting in Xamarin.Android 10.3, "System.MemberAccessException: Cannot create an instance of ... `1[T]" in Release configuration if JavaTypeScanner.GetJavaTypes() happens to list a generic subclass of a binding before the binding itself #4660

Description

@brendanzagaeski

Context: #4596 (comment)

Steps to reproduce

  1. Download and unzip the attached test case.
  2. Ensure that a hardware device is attached and that adb is running.
  3. msbuild -restore -p:Configuration=Release -t:Install AndroidSingleViewApp1\AndroidSingleViewApp1.csproj
    
  4. Launch the app by tapping the app icon on the target device.

Test case: JavaToManagedDuplicatesRelease.zip

Test case details

The test case uses a bindings library for a single Java class. The original Java class is defined as:

package com.contoso.javalibrary1;

public class Class1 {
    public static Class1 Create()
    {
        return new Class1();
    }
}

The library adds a generic subclass of the binding in C#:

[Register("com/contoso/javalibrary1/Class1", DoNotGenerateAcw = true)]
public partial class Class0<T> : Class1
{
    public Class0(IntPtr handle, JniHandleOwnership transfer)
        : base(handle, transfer)
    {
    }
}

This subclass intentionally uses 0 in the name to try to make JavaTypeScanner.GetJavaTypes() list it first.

Expected behavior

When built with Xamarin.Android 10.2, the application launches successfully and displays Hello World! in the center of the view.

Actual behavior

When built with Xamarin.Android 10.3, the application exits during launch:

FATAL EXCEPTION: main
Process: com.companyname.androidsingleviewapp1, PID: 24068
android.runtime.JavaProxyThrowable: System.MemberAccessException: Cannot create an instance of Com.Contoso.Javalibrary1.Class0`1[T] because Type.ContainsGenericParameters is true.
  at System.Reflection.RuntimeConstructorInfo.DoInvoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00054] in <78d6f78df71a498abab079fd455ad967>:0
  at System.Reflection.RuntimeConstructorInfo.Invoke (System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <78d6f78df71a498abab079fd455ad967>:0
  at System.Reflection.ConstructorInfo.Invoke (System.Object[] parameters) [0x00000] in <78d6f78df71a498abab079fd455ad967>:0
  at Java.Interop.TypeManager.CreateProxy (System.Type type, System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer) [0x0001b] in <2ccd59583214493fb4df51106833c4d9>:0
  at Java.Interop.TypeManager.CreateInstance (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer, System.Type targetType) [0x00111] in <2ccd59583214493fb4df51106833c4d9>:0
  at Java.Lang.Object.GetObject (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer, System.Type type) [0x00023] in <2ccd59583214493fb4df51106833c4d9>:0
  at Java.Lang.Object._GetObject[T] (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer) [0x00017] in <2ccd59583214493fb4df51106833c4d9>:0
  at Java.Lang.Object.GetObject[T] (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer) [0x00000] in <2ccd59583214493fb4df51106833c4d9>:0
  at Com.Contoso.Javalibrary1.Class1.Create () [0x0001e] in <8dad429b6bb14f3ab7952159824bc769>:0
  at AndroidSingleViewApp1.MainActivity.OnCreate (Android.OS.Bundle savedInstanceState) [0x00012] in <dc89185e1acd4e22bcef03b0baa45e56>:0
  at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_savedInstanceState) [0x0000f] in <2ccd59583214493fb4df51106833c4d9>:0
  at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.1(intptr,intptr,intptr)
       at crc647d7dcab8da8ee782.MainActivity.n_onCreate(Native Method)
       at crc647d7dcab8da8ee782.MainActivity.onCreate(MainActivity.java:29)
       at android.app.Activity.performCreate(Activity.java:7144)
       at android.app.Activity.performCreate(Activity.java:7135)
       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2931)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3086)
       at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
       at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
       at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1816)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loop(Looper.java:193)
       at android.app.ActivityThread.main(ActivityThread.java:6718)
       at java.lang.reflect.Method.invoke(Native Method)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

Preliminary investigation

It appears the issue is that JavaTypeScanner.GetJavaTypes() returns Class0<T> earlier in the list than Class1 during <GenerateJavaStubs/>:

https://github.com/xamarin/xamarin-android/blob/0fe28fb28c403b6c4addce2e9b728f077128c48f/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs#L173

Here's an excerpt of the values I see in allJavaTypes in my local debugging session:

[198]	{Com.Contoso.Javalibrary1.Class0`1}	Mono.Cecil.TypeDefinition
[199]	{Com.Contoso.Javalibrary1.BuildConfig}	Mono.Cecil.TypeDefinition
[200]	{Com.Contoso.Javalibrary1.Class1}	Mono.Cecil.TypeDefinition

The types are passed in this same order to TypeMapGenerator.GenerateRelease(), so the first match is added to moduleData.TypesScratch, while any other matches are added to moduleData.DuplicateTypes:

https://github.com/xamarin/xamarin-android/blob/0fe28fb28c403b6c4addce2e9b728f077128c48f/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs#L343-L350

Eventually this means that TypeMappingReleaseNativeAssemblyGenerator.WriteJavaMap() writes the MetadataToken of Class0<T> into typemaps.arm64-v8a.s instead of the MetadataToken of Class1

Version information

  • Xamarin.Android SDK 10.3.0.80 (d16-6/0fe28fb)
    • Java.Interop: xamarin/java.interop/d16-6@2cab35c

The issue also occurs when using the fixes from the candidate .vsix installer for #4656:

  • Xamarin.Android SDK 10.4.100.6 (remotes/pull/4656/merge/5266fab)
    • Java.Interop: xamarin/java.interop/master@377c4c7

This is expected because the changes in #4656 were only targeted for Debug configuration builds. (And indeed those fixes seemed to be effective for the DrawableTinting sample in the Debug configuration in my local environment.)

VS bug #1119273

Activity

  1. added this to the d16-6 milestone on May 7, 2020
  2. grendello commented on May 7, 2020

    @grendello
    Contributor

    Good test case @brendanzagaeski, thanks! However, the issue here is not one of duplication but rather of an open generic type being placed in the map, only to attempt to instantiate it later during the runtime and throw the exception you show. The fix here would be to go up the inheritance tree and put Class1 MVID:TokenID in the map for the open generic type instead. @jonpryor, would that be the correct course of action here?

  3. grendello commented on May 7, 2020

    @grendello
    Contributor

    The old typemap generator from Java.Interop skips types with generic parameters altogether. I'll update #4656 to do the same.

  4. added
    vs-syncFor internal use only; creates a VSTS "mirror" issue.
    on May 7, 2020
  5. added a commit that references this issue on May 7, 2020
  6. brendanzagaeski commented on May 15, 2020

    @brendanzagaeski
    ContributorAuthor

    Release status update

    A Preview version of Xamarin.Android has now been published on macOS that includes the fix for this item.

    The fix is not yet available on Windows. I will update this item again when a version with the fix is available on Windows.

    Fix included in Xamarin.Android 10.3.1.0.

    Fix included on macOS in Visual Studio 2019 for Mac version 8.6 Preview 6. To try the Preview version that includes the fix, check for the latest updates on the Preview updater channel.

    Fix not yet available on Windows.

  7. brendanzagaeski commented on Jun 3, 2020

    @brendanzagaeski
    ContributorAuthor

    Release status update

    A new Release version of Xamarin.Android has now been published on both Windows and macOS that includes the fix for this item.

    Fix included in Xamarin.Android 10.3.1.0.

    Fix included on Windows in Visual Studio 2019 version 16.6. To get the new version that includes the fix, check for the latest updates or install the latest version from https://visualstudio.microsoft.com/downloads/.

    Fix included on macOS in Visual Studio 2019 for Mac version 8.6. To get the new version that includes the fix, check for the latest updates on the Stable updater channel.

  8. removed
    vs-syncFor internal use only; creates a VSTS "mirror" issue.
    on Jun 11, 2020
  9. ghost locked as resolved and limited conversation to collaborators on Jun 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions