From e85314ab21c98baaedb18bef2c6fa1931a07d50e Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Fri, 11 Aug 2017 12:01:37 -0400 Subject: [PATCH] [Mono.Android] Define JavaCollection before ICollection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://bugzilla.xamarin.com/show_bug.cgi?id=58303 Background: It is possible for a Java type to have "aliasing bindings": two or more managed types which claim to bind the same Java type: // C# namespace Android.Runtime { [Register ("java/util/HashMap", DoNotRegisterAcw=true)] partial class JavaDictionary { } } namespace Java.Util { [Register ("java/util/HashMap", DoNotRegisterAcw=true)] partial class HashMap { } } These can't be realistically forbidden. Enter a new-to-bind Java type: // Java public class Bxc58383 extends java.util.HashMap implements java.util.Map { } In order to bind the above `Bxc58383` type, `generator` needs to determine the appropriate binding type to use for `java.util.HashMap`. `generator`'s approach to supporting aliasing types is (largely) to ignore the problem (almost) entirely: There Can Be Only Oneā„¢ mapping from a Java type to a managed type, so `generator` needs to pick one. It does so by using the *last* definition encountered in the assembly. Consequently, for the above set of C# declarations, when `generator` needs to find the managed type which binds `java.util.HashMap`, `Java.Util.HashMap` will be chosen, as it is the last declared type. Unfortunately, "real life" is a bit more complicated: `Mono.Android.dll` is made up of *thousands* of files, and the order of types within an assembly is a compiler implementation detail. As such...the current `Mono.Android.dll` has `JavaDictionary` defined *after* `HashMap`: # output truncated for relevance $ monodis --typedef bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v7.1/Mono.Android.dll | grep 'Dictionary\|HashMap' 1270: Java.Util.Dictionary (flist=12611, mlist=29569, flags=0x100081, extends=0x20b8) 1287: Java.Util.HashMap (flist=12662, mlist=29806, flags=0x100001, extends=0x1388) 3992: Android.Runtime.JavaDictionary (flist=32135, mlist=85344, flags=0x100001, extends=0x20b8) Because `JavaDictionary` is defined *after* `HashMap`, and both of those types have `[Register]` attributes which declare that they bind the *same* Java type, the result is that the binding of `Bxc58383` is horrifically broken: // C# partial class Bxc58383 : global::Android.Runtime.JavaDictionary, global::Java.Util.IMap { } Unfortunately, `JavaDictionary` doesn't itself implement `IMap`, so: error CS0535: 'Bxc58383' does not implement interface member 'IMap.ContainsKey(Object)' along with 10 other related errors. Update the `src/Mono.Android` build process so that `JavaDictionary` and related types will be defined within `Mono.Android.dll` *before* all the generated types. This allows `HashMap` to be defined last, allowing `Bxc58383` to be bound without error: # output truncated $ monodis --typedef bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v7.1/Mono.Android.dll | grep 'Dictionary\|HashMap' 3703: Android.Runtime.JavaDictionary (flist=34089, mlist=78134, flags=0x100001, extends=0x4e08) 4170: Java.Util.Dictionary (flist=39685, mlist=91737, flags=0x100081, extends=0x4e08) 4187: Java.Util.HashMap (flist=39736, mlist=91974, flags=0x100001, extends=0x40d8) --- src/Mono.Android/Mono.Android.csproj | 28 +++++++++---------- .../Xamarin.Android.McwGen-Tests.csproj | 1 + .../java/com/xamarin/android/Bxc58383.java | 7 +++++ 3 files changed, 22 insertions(+), 14 deletions(-) create mode 100644 tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/Bxc58383.java diff --git a/src/Mono.Android/Mono.Android.csproj b/src/Mono.Android/Mono.Android.csproj index 1c984aded43..0b743ba4789 100644 --- a/src/Mono.Android/Mono.Android.csproj +++ b/src/Mono.Android/Mono.Android.csproj @@ -75,6 +75,20 @@ False + + + + + False + $(IntermediateOutputPath)android-$(AndroidApiLevel)\ + + + + $([System.IO.Path]::GetFullPath ('$(OutputPath)$(AssemblyName).dll')) + CoreBuild + + + @@ -311,20 +325,6 @@ - - - - - False - $(IntermediateOutputPath)android-$(AndroidApiLevel)\ - - - - $([System.IO.Path]::GetFullPath ('$(OutputPath)$(AssemblyName).dll')) - CoreBuild - - - {3FC3E78B-F7D4-42EA-BBE8-4535DF42BFF8} diff --git a/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/Xamarin.Android.McwGen-Tests.csproj b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/Xamarin.Android.McwGen-Tests.csproj index c7aa39f8ba7..206fafa9781 100644 --- a/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/Xamarin.Android.McwGen-Tests.csproj +++ b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/Xamarin.Android.McwGen-Tests.csproj @@ -76,6 +76,7 @@ + diff --git a/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/Bxc58383.java b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/Bxc58383.java new file mode 100644 index 00000000000..17e6dd73bf3 --- /dev/null +++ b/tests/CodeGen-Binding/Xamarin.Android.McwGen-Tests/java/com/xamarin/android/Bxc58383.java @@ -0,0 +1,7 @@ +package com.xamarin.android; + +public class Bxc58383 + extends java.util.HashMap + implements java.util.Map +{ +}