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

Closed
@brendanzagaeski

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions