Skip to content

Commit

Permalink
Bump to xamarin/java.interop/main@07c73009 (#8681)
Browse files Browse the repository at this point in the history
Context: dotnet/java-interop#1165
Context: dotnet/java-interop@005c914
Context: #8543
Context: dotnet/java-interop@07c7300
Context: #8625
Context: xamarin/monodroid@e3e4f12
Context: xamarin/monodroid@a04b73b
Context: efbec22

Changes: dotnet/java-interop@8b85462...07c7300

  * dotnet/java-interop@07c73009: [Java.Interop] Typemap support for JavaObject & `[JniTypeSignature]` (dotnet/java-interop#1181)
  * dotnet/java-interop@d529f3be: Bump to xamarin/xamarin-android-tools/main@ed102fc (dotnet/java-interop#1182)
  * dotnet/java-interop@def5bc0d: [ci] Add API Scan job (dotnet/java-interop#1178)
  * dotnet/java-interop@d5afa0af: [invocation-overhead] Add generated source files (dotnet/java-interop#1175)
  * dotnet/java-interop@473ef74c: Bump to xamarin/xamarin-android-tools/main@4889bf0 (dotnet/java-interop#1172)
  * dotnet/java-interop@005c9141: [Java.Interop] Avoid `Type.GetType()` in `ManagedPeer` (dotnet/java-interop#1168)
  * dotnet/java-interop@0f1efebd: [Java.Interop] Use PublicApiAnalyzers to ensure we do not break API (dotnet/java-interop#1170)

(From the "infinite scream" department…)

It started with a desire to remove some linker warnings
(dotnet/java-interop#1165):

	external/Java.Interop/src/Java.Interop/Java.Interop/ManagedPeer.cs(93,19,93,112):
	warning IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'.
	It's not possible to guarantee the availability of the target type.

dotnet/java-interop@005c9141 attempted to fix this by requiring the
use of "typemaps" mapping Java type signatures to managed types,
replacing e.g.:

	Type            type            = Type.GetType ("Example.Type, AssemblyName", throwOnError: true)!;
	Type[]          parameterTypes  = GetParameterTypes ("System.Int32:System.Int32");
	ConstructorInfo ctor            = type.GetConstructor (ptypes);
	// ctor=Example.Type(int, int) constructor

with (not exactly, but for expository purposes):

	Type            type            = GetTypeFromSignature("crc64…/Type");
	Type[]          parameterTypes  = GetConstructorCandidateParameterTypes ("(II)V");
	ConstructorInfo ctor            = type.GetConstructor (ptypes);
	// ctor=Example.Type(int, int) constructor
	
among other changes.

This was a *significant* change that would alter *Java.Interop*
semantics but *not* .NET Android semantics -- .NET Android uses
`Java.Interop.TypeManager.n_Activate()` (in this repo) for Java-side
"activation" scenarios, not `Java.Interop.ManagedPeer` -- so in an
abundance of caution we did a manual integration test in
#8543 to make sure nothing broke before
merging it.

Something was apparently "off" in that integration.  (We're still not
sure what was off, or why it was completely green.)

Ever since dotnet/java-interop@005c9141 was merged, every attempt to
bump xamarin/Java.Interop has failed, in a number of ways described
below.  However, instead of reverting dotnet/java-interop@005c9141
we took this as an opportunity to understand *how and why* things
were failing, as apparently we had encountered some *long-standing*
corner cases in How Things Work.

The oversights and failures include:

 1. In order to make the Java.Interop unit tests work in .NET Android,
    the (largely hand-written) Java.Interop test types *also* need to
    participate with .NET Android typemap support, so that there is a
    typemap entry mapping `net/dot/jni/test/GenericHolder` to
    `Java.InteropTests.GenericHolder<T>` and vice-versa.

    dotnet/java-interop@07c73009 updates
    `Java.Interop.Tools.JavaCallableWrappers` to support creating
    typemap entries for `Java.Interop.JavaObject` subclasses,
    introducing a new `TypeDefinition.HasJavaPeer()` extension method.

 2. (1) meant that, for the first time ever, types in
    `Java.Interop-Tests` participated in .NET Android type mapping.
    This *sounds* fine, except that `Java.Interop-Tests` contains
    "competing bindings" for `java.lang.Object`:

        [JniTypeSignature ("java/lang/Object", GenerateJavaPeer=false)]
        partial class JavaLangRemappingTestObject : JavaObject {
        }

 3. (2) means that, for the first time ever, we *could* have the
    typemap entry for `java/lang/Object` map to
    `Java.InteropTests.JavaLangRemappingTestObject, Java.Interop-Tests`,
    *not* `Java.Lang.Object, Mono.Android`.

    Arguably a bug, arguably "meh", but this setup triggered some
    never previously encountered error conditions:

 4. `EmbeddedAssemblies::typemap_java_to_managed()` within
    `libmonodroid.so` returns a `System.Type` that corresponds to a
    JNI type.  `typemap_java_to_managed()` has a bug/corner case
    wherein it will only provide `Type` instances from assemblies
    which have already been loaded.

    Early in startup, `Java.Interop-Tests` hasn't been loaded yet, so
    when `java/lang/Object` was mapped to
    `Java.InteropTests.JavaLangRemappingTestObject, Java.Interop-Tests`,
    `typemap_java_to_managed()` would return `null`.

    This is a bug/corner case, which is being investigated in
    #8625.

 5. Calls to `Java.Lang.Object.GetObject<T>()` call
    `Java.Interop.TypeManager.CreateInstance()`, which loops through
    the type and all base types to find a known binding/wrapper.
    Because of (3)+(4), if (when) we try to find the wrapper for
    `java/lang/Object`, we would find *no* mapping.

    This would cause an `JNI DETECTED ERROR IN APPLICATION` *crash*.

    This was due to a "use after free" bug.

    See the "TypeManager.CreateInstance() Use After Free Bug" section.

 6. Once (5) is fixed we encounter our next issue: the
    `Java.InteropTests.JnienvTest.NewOpenGenericTypeThrows()` unit
    test started failing because
    `crc641855b07eca6dcc03.GenericHolder_1` couldn't be found.

    This was caused by a bug in `acw-map.txt` parsing within `<R8/>`.

    See the "`<R8/>` and `acw-map.txt` parsing.`" section.

 7. Once (6) was fixed, (3) caused a *new* set of failures:
    multiple tests started failing because `java/lang/Object` was
    being mapped to the wrong managed type.

    (3) becomes less "meh" and more "definitely a bug".

    See the "Correct `java/lang/Object` mappings" section.

*Now* things should work reliably.


~~ TypeManager.CreateInstance() Use After Free Bug ~~

On 2011-Oct-19, xamarin/monodroid@e3e4f123d8 introduced a
use-after-free bug within `TypeManager.CreateInstance()`:

	JNIEnv.DeleteRef (handle, transfer);
	throw new NotSupportedException (
	        FormattableString.Invariant ($"Internal error finding wrapper class for '{JNIEnv.GetClassNameFromInstance (handle)}'. (Where is the Java.Lang.Object wrapper?!)"),
	        CreateJavaLocationException ());

`handle` *cannot be used* after `JNIEnv.DeleteRef(handle)`.
Failure to do so results in a `JNI DETECTED ERROR IN APPLICATION`
crash; with `adb shell setprop debug.mono.log lref+` set, we see:

	I monodroid-lref: +l+ lrefc 1 handle 0x71/L from thread '(null)'(1)
	D monodroid-gref:    at Android.Runtime.AndroidObjectReferenceManager.CreatedLocalReference(JniObjectReference , Int32& )
	D monodroid-gref:    at Java.Interop.JniRuntime.JniObjectReferenceManager.CreatedLocalReference(JniEnvironmentInfo , JniObjectReference )
	D monodroid-gref:    at Java.Interop.JniEnvironment.LogCreateLocalRef(JniObjectReference )
	D monodroid-gref:    at Java.Interop.JniEnvironment.LogCreateLocalRef(IntPtr )
	D monodroid-gref:    at Java.Interop.JniEnvironment.InstanceMethods.CallObjectMethod(JniObjectReference , JniMethodInfo )
	D monodroid-gref:    …
	…
	I monodroid-lref: -l- lrefc 0 handle 0x71/L from thread '(null)'(1)
	D monodroid-gref:    at Android.Runtime.AndroidObjectReferenceManager.DeleteLocalReference(JniObjectReference& , Int32& )
	D monodroid-gref:    at Java.Interop.JniRuntime.JniObjectReferenceManager.DeleteLocalReference(JniEnvironmentInfo , JniObjectReference& )
	D monodroid-gref:    at Java.Interop.JniObjectReference.Dispose(JniObjectReference& reference)
	D monodroid-gref:    at Android.Runtime.JNIEnv.DeleteLocalRef(IntPtr )
	D monodroid-gref:    at Android.Runtime.JNIEnv.DeleteRef(IntPtr , JniHandleOwnership )
	D monodroid-gref:    at Java.Interop.TypeManager.CreateInstance(IntPtr , JniHandleOwnership , Type )
	D monodroid-gref:    at Java.Lang.Object.GetObject(IntPtr , JniHandleOwnership , Type )
	D monodroid-gref:    at Java.Lang.Object._GetObject[IIterator](IntPtr , JniHandleOwnership )
	D monodroid-gref:    at Java.Lang.Object.GetObject[IIterator](IntPtr handle, JniHandleOwnership transfer)
	D monodroid-gref:    …
	D monodroid-gref:
	E droid.NET_Test: JNI ERROR (app bug): accessed stale Local 0x71  (index 7 in a table of size 7)
	F droid.NET_Test: java_vm_ext.cc:570] JNI DETECTED ERROR IN APPLICATION: use of deleted local reference 0x71
	…
	F droid.NET_Test: runtime.cc:630]   native: #13 pc 00000000003ce865  /apex/com.android.runtime/lib64/libart.so (art::(anonymous namespace)::CheckJNI::GetObjectClass(_JNIEnv*, _jobject*)+837)

The immediate fix is Don't Do That™; use a temporary:

	class_name = JNIEnv.GetClassNameFromInstance (handle);
	JNIEnv.DeleteRef (handle, transfer);
	throw new NotSupportedException (
	        FormattableString.Invariant ($"Internal error finding wrapper class for '{class_name}'. (Where is the Java.Lang.Object wrapper?!)"),
	        CreateJavaLocationException ());

Unfortunately, *just* fixing the "use-after-free" bug is insufficient;
if we throw that `NotSupportedException`, things *will* break
elsewhere.  We'll just have an "elegant unhandled exception" app crash
instead of a "THE WORLD IS ENDING" failed assertion crash.

We could go with the simple fix for the crash, but this means that in
order to integrate dotnet/java-interop@005c9141 &
dotnet/java-interop@07c73009 we'd have to figure out how to *ensure*
that `java/lang/Object` is bound as `Java.Lang.Object, Mono.Android`,
not `Java.InteropTests.JavaLangRemappingTestObject, Java.Interop-Tests`.
(We actually need to do this *anyway*; see the
"Correct `java/lang/Object` mappings" section.  At the time we I was
trying to *avoid* special-casing `Mono.Android.dll`…)

There is a*slightly* more complicated approach which fixes (5)
while supporting (4) `typemap_java_to_managed()` returning null;
consider the `-l-` callstack:

	at Android.Runtime.JNIEnv.DeleteRef(IntPtr , JniHandleOwnership )
	at Java.Interop.TypeManager.CreateInstance(IntPtr , JniHandleOwnership , Type )
	at Java.Lang.Object.GetObject(IntPtr , JniHandleOwnership , Type )
	at Java.Lang.Object._GetObject[IIterator](IntPtr , JniHandleOwnership )
	at Java.Lang.Object.GetObject[IIterator](IntPtr handle, JniHandleOwnership transfer)
	at Android.Runtime.JavaSet.Iterator()

This is part of a generic `Object.GetObject<IIterator>()` invocation!
Additionally, because `IIterator` is an interface, in *normal* use
the `type` variable within `TypeManager.CreateInstance()` would be
`Java.Lang.Object, Mono.Android` and then *immediately discarded*
because `Java.Lang.Object` cannot be assigned to `IIterator`.

Moving the type compatibility check to *before* the
`type == null` check fixes *an* issue with `typemap_java_to_managed()`
returning null.


~~ `<R8/>` and `acw-map.txt` parsing.` ~~

There are many ways for Android+Java code to refer to managed types.

For example, consider the following View subclass:

	namespace Example {
	  partial class MyCoolView : Android.Views.View {
	    // …
	  }
	}

Within layout `.axml` files, you can mention an `Android.Views.View`
subclass by:

  * Using the .NET Full Class Name as an element name.

        <Example.MyCoolView />

  * Using the .NET Full Class Name with a *lowercased* namespace
    name as the element name.

        <example.MyCoolView />

  * Use the Java-side name directly.

        <crc64….NiftyView />

Within Fragments, you can also use the *assembly-qualified name*:

	<fragment class="Example.MyCoolView, AssemblyName" />

At build time, all instances of the .NET type names will be
*replaced* with the Java type names before the Android toolchain
processes the files.

The association between .NET type names and Java names is stored
within `$(IntermediateOutputPath)acw-map.txt`, which was introduced
in xamarin/monodroid@a04b73b3.

*Normally* `acw-map.txt` contains three entries:

 1. The fully-qualified .NET type name
 2. The .NET type name, no assembly
 3. (2) with a lowercased namespace name, *or* the `[Register]`
    value, if provided.

For example:

	Mono.Android_Test.Library.CustomTextView, Mono.Android-Test.Library.NET;crc6456ab8145c81c4100.CustomTextView
	Mono.Android_Test.Library.CustomTextView;crc6456ab8145c81c4100.CustomTextView   
	mono.android_test.library.CustomTextView;crc6456ab8145c81c4100.CustomTextView   
	Java.InteropTests.GenericHolder`1, Java.Interop-Tests;net.dot.jni.test.tests.GenericHolder
	Java.InteropTests.GenericHolder`1;net.dot.jni.test.tests.GenericHolder          
	net.dot.jni.test.tests.GenericHolder;net.dot.jni.test.tests.GenericHolder    

However, when warning XA4214 is emitted (efbec22), there is a
"collision" on the .NET side (but *not* the Java side); (2) and (3)
are potentially *ambiguous*, so one .NET type is arbitrarily chosen.
(Collisions on the Java side result in XA4215 *errors*.)

The first line is still possible, because of assembly qualification.

Enter ``Java.InteropTests.GenericHolder`1``: this type is present in
*both* `Java.Interop-Tests.dll` *and* `Mono.Android-Tests.dll`.
dotnet/java-interop@07c73009, this was "fine" because the
`GenericHolder<T>` within `Java.Interop-Tests.dll` did not participate
in typemap generation.  Now it does, resulting in the XA4214 warning.
XA4214 *also* means that instead of three lines, it's *one* line:

	Java.InteropTests.GenericHolder`1, Mono.Android.NET-Tests;crc641855b07eca6dcc03.GenericHolder_1

Enter `<R8/>`, which parses `acw-map.txt` to create a
`proguard_project_primary.cfg` file.  `<R8/>` did it's *own* parsing
of `acw-map.txt`, parsing only *one of every three lines*, on the
assumption that *all* entries took three lines.

This breaks in the presence of XA4214, because some entries only take
one line, not three lines.  This in turn meant that
`proguard_project_primary.cfg` could *miss* types, which could mean
that `r8` would *remove* the unspecified types, resulting in
`ClassNotFoundException` at runtime:

	Java.Lang.ClassNotFoundException : crc641855b07eca6dcc03.GenericHolder_1
	----> Java.Lang.ClassNotFoundException : Didn't find class "crc641855b07eca6dcc03.GenericHolder_1" on path: DexPathList[[zip file "/data/app/Mono.Android.NET_Tests-2stBqO43ov5F6bHfYemJHQ==/base.apk", zip file "/data/app/Mono.Android.NET_Tests-2stBqO43ov5F6bHfYemJHQ==/split_config.x86_64.apk", zip file "/data/app/Mono.Android.NET_Tests-2stBqO43ov5F6bHfYemJHQ==/split_config.xxhdpi.apk"],nativeLibraryDirectories=[/data/app/Mono.Android.NET_Tests-2stBqO43ov5F6bHfYemJHQ==/lib/x86_64, /system/fake-libs64, /data/app/Mono.Android.NET_Tests-2stBqO43ov5F6bHfYemJHQ==/base.apk!/lib/x86_64, /data/app/Mono.Android.NET_Tests-2stBqO43ov5F6bHfYemJHQ==/split_config.x86_64.apk!/lib/x86_64, /data/app/Mono.Android.NET_Tests-2stBqO43ov5F6bHfYemJHQ==/split_config.xxhdpi.apk!/lib/x86_64, /system/lib64, /system/product/lib64]]
	   at Java.Interop.JniEnvironment.StaticMethods.CallStaticObjectMethod(JniObjectReference , JniMethodInfo , JniArgumentValue* )
	   at Android.Runtime.JNIEnv.FindClass(String )

Update `<R8/>` to instead use `MonoAndroidHelper.LoadMapFile()`,
which reads all lines within `acw-map.txt`.  This results in a
`proguard_project_primary.cfg` file which properly contains a `-keep`
entry for XA4214-related types, such as
`crc641855b07eca6dcc03.GenericHolder_1`.


~~ Correct `java/lang/Object` mappings ~~`

Previous valiant efforts to allow `java/lang/Object` to be mapped to
"anything", not just `Java.Lang.Object, Mono.Android`, eventually
resulted in lots of unit test failures, e.g.:

`Android.RuntimeTests.XmlReaderPullParserTest.ToLocalJniHandle()`:

	System.NotSupportedException : Unable to activate instance of type Java.InteropTests.JavaLangRemappingTestObject from native handle 0x19 (key_handle 0x2408476).
	----> System.MissingMethodException : No constructor found for Java.InteropTests.JavaLangRemappingTestObject::.ctor(System.IntPtr, Android.Runtime.JniHandleOwnership)
	----> Java.Interop.JavaLocationException : Exception_WasThrown, Java.Interop.JavaLocationException
	   at Java.Interop.TypeManager.CreateInstance(IntPtr , JniHandleOwnership , Type )
	   at Java.Interop.TypeManager.CreateInstance(IntPtr , JniHandleOwnership )
	   at Android.Runtime.XmlResourceParserReader.FromNative(IntPtr , JniHandleOwnership )
	   at Android.Runtime.XmlResourceParserReader.FromJniHandle(IntPtr handle, JniHandleOwnership transfer)
	   at Android.Content.Res.Resources.GetXml(Int32 )
	   at Android.RuntimeTests.XmlReaderPullParserTest.ToLocalJniHandle()
	   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
	   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object , BindingFlags )
	--MissingMethodException
	   at Java.Interop.TypeManager.CreateProxy(Type , IntPtr , JniHandleOwnership )
	   at Java.Interop.TypeManager.CreateInstance(IntPtr , JniHandleOwnership , Type )

With a partially heavy heart, we need to special-case typemap entries
by processing `Mono.Android.dll` *first*, so that it gets first dibs
at bindings for `java/lang/Object` and other types.

Update `NativeTypeMappingData` to process types from `Mono.Android`
before processing any other module.

Note that the special-casing needs to happen in `NativeTypeMappingData`
because typemaps were formerly processed in *sorted module order*, in
which the sort order is based on the *byte representation* of the
module's MVID (a GUID).  Additionally, *linking changes the MVID*,
which means module order is *effectively random*.  Consequently,
trying to special case typemap ordering anywhere else is ineffective.


~~ Other ~~

Update `JavaCompileToolTask` to log the contents of its response file.

Update LLVM-IR -related types within
`src/Xamarin.Android.Build.Tasks/Utilities` to use `TaskLoggingHelper`
for logging purposes, *not* `Action<string>`.  Update related types
to accept `TaskLoggingHelper`, so that we can more easily add
diagnostic messages to these types in the future.
  • Loading branch information
jonpryor authored Feb 2, 2024
1 parent 1858b55 commit 25d1f00
Show file tree
Hide file tree
Showing 19 changed files with 110 additions and 46 deletions.
2 changes: 1 addition & 1 deletion external/Java.Interop
12 changes: 8 additions & 4 deletions src/Mono.Android/Java.Interop/TypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -286,16 +286,20 @@ internal static IJavaPeerable CreateInstance (IntPtr handle, JniHandleOwnership

JNIEnv.DeleteLocalRef (class_ptr);

if (targetType != null &&
(type == null ||
!targetType.IsAssignableFrom (type))) {
type = targetType;
}

if (type == null) {
class_name = JNIEnv.GetClassNameFromInstance (handle);
JNIEnv.DeleteRef (handle, transfer);
throw new NotSupportedException (
FormattableString.Invariant ($"Internal error finding wrapper class for '{JNIEnv.GetClassNameFromInstance (handle)}'. (Where is the Java.Lang.Object wrapper?!)"),
FormattableString.Invariant ($"Internal error finding wrapper class for '{class_name}'. (Where is the Java.Lang.Object wrapper?!)"),
CreateJavaLocationException ());
}

if (targetType != null && !targetType.IsAssignableFrom (type))
type = targetType;

if (type.IsInterface || type.IsAbstract) {
var invokerType = JavaObjectExtensions.GetInvokerType (type);
if (invokerType == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ void GenerateCompressedAssemblySources ()

void Generate (IDictionary<string, CompressedAssemblyInfo> dict)
{
var composer = new CompressedAssembliesNativeAssemblyGenerator (dict);
var composer = new CompressedAssembliesNativeAssemblyGenerator (Log, dict);
LLVMIR.LlvmIrModule compressedAssemblies = composer.Construct ();

foreach (string abi in SupportedAbis) {
Expand Down
2 changes: 1 addition & 1 deletion src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ void SaveResource (string resource, string filename, string destDir, Func<string

void WriteTypeMappings (List<JavaType> types, TypeDefinitionCache cache)
{
var tmg = new TypeMapGenerator ((string message) => Log.LogDebugMessage (message), SupportedAbis);
var tmg = new TypeMapGenerator (Log, SupportedAbis);
if (!tmg.Generate (Debug, SkipJniAddNativeMethodRegistrationAttributeScan, types, cache, TypemapOutputDirectory, GenerateNativeAssembly, out ApplicationConfigTaskState appConfState)) {
throw new XamarinAndroidException (4308, Properties.Resources.XA4308);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public override bool RunTask ()

void GenerateEmpty ()
{
Generate (new JniRemappingAssemblyGenerator (), typeReplacementsCount: 0);
Generate (new JniRemappingAssemblyGenerator (Log), typeReplacementsCount: 0);
}

void Generate ()
Expand All @@ -74,7 +74,7 @@ void Generate ()
}
}

Generate (new JniRemappingAssemblyGenerator (typeReplacements, methodReplacements), typeReplacements.Count);
Generate (new JniRemappingAssemblyGenerator (Log, typeReplacements, methodReplacements), typeReplacements.Count);
}

void Generate (JniRemappingAssemblyGenerator jniRemappingComposer, int typeReplacementsCount)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,13 +386,13 @@ void AddEnvironment ()

if (enableMarshalMethods) {
marshalMethodsAsmGen = new MarshalMethodsNativeAssemblyGenerator (
Log,
assemblyCount,
uniqueAssemblyNames,
marshalMethodsState?.MarshalMethods,
Log
marshalMethodsState?.MarshalMethods
);
} else {
marshalMethodsAsmGen = new MarshalMethodsNativeAssemblyGenerator (assemblyCount, uniqueAssemblyNames);
marshalMethodsAsmGen = new MarshalMethodsNativeAssemblyGenerator (Log, assemblyCount, uniqueAssemblyNames);
}
LLVMIR.LlvmIrModule marshalMethodsModule = marshalMethodsAsmGen.Construct ();

Expand Down
4 changes: 4 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Tasks/JavaCompileToolTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ private void GenerateResponseFile ()
file.Replace (@"\", @"\\").Normalize (NormalizationForm.FormC)));
}
}
Log.LogDebugMessage ($"javac response file contents: {TemporarySourceListFile}");
foreach (var line in File.ReadLines (TemporarySourceListFile)) {
Log.LogDebugMessage ($" {line}");
}
}
}
}
18 changes: 9 additions & 9 deletions src/Xamarin.Android.Build.Tasks/Tasks/R8.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.Collections.Generic;
Expand Down Expand Up @@ -82,16 +83,15 @@ protected override CommandLineBuilder GetCommandLineBuilder ()

if (EnableShrinking) {
if (!string.IsNullOrEmpty (AcwMapFile)) {
var acwLines = File.ReadAllLines (AcwMapFile);
var acwMap = MonoAndroidHelper.LoadMapFile (BuildEngine4, Path.GetFullPath (AcwMapFile), StringComparer.OrdinalIgnoreCase);
var javaTypes = new List<string> (acwMap.Values.Count);
foreach (var v in acwMap.Values) {
javaTypes.Add (v);
}
javaTypes.Sort (StringComparer.Ordinal);
using (var appcfg = File.CreateText (ProguardGeneratedApplicationConfiguration)) {
for (int i = 0; i + 2 < acwLines.Length; i += 3) {
try {
var line = acwLines [i + 2];
var java = line.Substring (line.IndexOf (';') + 1);
appcfg.WriteLine ("-keep class " + java + " { *; }");
} catch {
// skip invalid lines
}
foreach (var java in javaTypes) {
appcfg.WriteLine ($"-keep class {java} {{ *; }}");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Java.Interop.Tools.TypeNameMappings;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.Android.Build.Tasks;
using Xamarin.Android.Tasks.LLVMIR;

namespace Xamarin.Android.Tasks
Expand Down Expand Up @@ -135,7 +136,6 @@ sealed class XamarinAndroidBundledAssembly

SortedDictionary <string, string>? environmentVariables;
SortedDictionary <string, string>? systemProperties;
TaskLoggingHelper log;
StructureInstance? application_config;
List<StructureInstance<DSOCacheEntry>>? dsoCache;
List<StructureInstance<XamarinAndroidBundledAssembly>>? xamarinAndroidBundledAssemblies;
Expand Down Expand Up @@ -172,6 +172,7 @@ sealed class XamarinAndroidBundledAssembly
public bool MarshalMethodsEnabled { get; set; }

public ApplicationConfigNativeAssemblyGenerator (IDictionary<string, string> environmentVariables, IDictionary<string, string> systemProperties, TaskLoggingHelper log)
: base (log)
{
if (environmentVariables != null) {
this.environmentVariables = new SortedDictionary<string, string> (environmentVariables, StringComparer.Ordinal);
Expand All @@ -180,8 +181,6 @@ public ApplicationConfigNativeAssemblyGenerator (IDictionary<string, string> env
if (systemProperties != null) {
this.systemProperties = new SortedDictionary<string, string> (systemProperties, StringComparer.Ordinal);
}

this.log = log;
}

protected override void Construct (LlvmIrModule module)
Expand Down Expand Up @@ -322,7 +321,7 @@ List<StructureInstance<DSOCacheEntry>> InitDSOCache ()
continue;
}

dsos.Add ((name, $"dsoName{dsos.Count.ToString (CultureInfo.InvariantCulture)}", ELFHelper.IsEmptyAOTLibrary (log, item.ItemSpec)));
dsos.Add ((name, $"dsoName{dsos.Count.ToString (CultureInfo.InvariantCulture)}", ELFHelper.IsEmptyAOTLibrary (Log, item.ItemSpec)));
}

var dsoCache = new List<StructureInstance<DSOCacheEntry>> ();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;

using Microsoft.Build.Utilities;

using Xamarin.Android.Tasks.LLVMIR;

namespace Xamarin.Android.Tasks
Expand Down Expand Up @@ -66,7 +68,8 @@ sealed class CompressedAssemblies
StructureInfo compressedAssemblyDescriptorStructureInfo;
StructureInfo compressedAssembliesStructureInfo;

public CompressedAssembliesNativeAssemblyGenerator (IDictionary<string, CompressedAssemblyInfo> assemblies)
public CompressedAssembliesNativeAssemblyGenerator (TaskLoggingHelper log, IDictionary<string, CompressedAssemblyInfo> assemblies)
: base (log)
{
this.assemblies = assemblies;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Text;

using Microsoft.Build.Utilities;

using Xamarin.Android.Tasks.LLVMIR;

namespace Xamarin.Android.Tasks
Expand Down Expand Up @@ -190,10 +192,12 @@ sealed class JniRemappingTypeReplacementEntry

public int ReplacementMethodIndexEntryCount { get; private set; } = 0;

public JniRemappingAssemblyGenerator ()
public JniRemappingAssemblyGenerator (TaskLoggingHelper log)
: base (log)
{}

public JniRemappingAssemblyGenerator (List<JniRemappingTypeReplacement> typeReplacements, List<JniRemappingMethodReplacement> methodReplacements)
public JniRemappingAssemblyGenerator (TaskLoggingHelper log, List<JniRemappingTypeReplacement> typeReplacements, List<JniRemappingMethodReplacement> methodReplacements)
: base (log)
{
this.typeReplacementsInput = typeReplacements ?? throw new ArgumentNullException (nameof (typeReplacements));
this.methodReplacementsInput = methodReplacements ?? throw new ArgumentNullException (nameof (methodReplacements));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System.IO.Hashing;
using System.Text;

using Microsoft.Build.Utilities;

using Xamarin.Android.Tools;

namespace Xamarin.Android.Tasks.LLVMIR
Expand All @@ -11,6 +13,13 @@ abstract class LlvmIrComposer
{
bool constructed;

protected readonly TaskLoggingHelper Log;

protected LlvmIrComposer (TaskLoggingHelper log)
{
this.Log = log ?? throw new ArgumentNullException (nameof (log));
}

protected abstract void Construct (LlvmIrModule module);

public LlvmIrModule Construct ()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,6 @@ public MarshalMethodAssemblyIndexValuePlaceholder (MarshalMethodInfo mmi, Assemb
ICollection<string> uniqueAssemblyNames;
int numberOfAssembliesInApk;
IDictionary<string, IList<MarshalMethodEntry>> marshalMethods;
TaskLoggingHelper logger;

StructureInfo marshalMethodsManagedClassStructureInfo;
StructureInfo marshalMethodNameStructureInfo;
Expand All @@ -243,7 +242,8 @@ public MarshalMethodAssemblyIndexValuePlaceholder (MarshalMethodInfo mmi, Assemb
/// <summary>
/// Constructor to be used ONLY when marshal methods are DISABLED
/// </summary>
public MarshalMethodsNativeAssemblyGenerator (int numberOfAssembliesInApk, ICollection<string> uniqueAssemblyNames)
public MarshalMethodsNativeAssemblyGenerator (TaskLoggingHelper log, int numberOfAssembliesInApk, ICollection<string> uniqueAssemblyNames)
: base (log)
{
this.numberOfAssembliesInApk = numberOfAssembliesInApk;
this.uniqueAssemblyNames = uniqueAssemblyNames ?? throw new ArgumentNullException (nameof (uniqueAssemblyNames));
Expand All @@ -254,12 +254,12 @@ public MarshalMethodsNativeAssemblyGenerator (int numberOfAssembliesInApk, IColl
/// <summary>
/// Constructor to be used ONLY when marshal methods are ENABLED
/// </summary>
public MarshalMethodsNativeAssemblyGenerator (int numberOfAssembliesInApk, ICollection<string> uniqueAssemblyNames, IDictionary<string, IList<MarshalMethodEntry>> marshalMethods, TaskLoggingHelper logger)
public MarshalMethodsNativeAssemblyGenerator (TaskLoggingHelper log, int numberOfAssembliesInApk, ICollection<string> uniqueAssemblyNames, IDictionary<string, IList<MarshalMethodEntry>> marshalMethods)
: base (log)
{
this.numberOfAssembliesInApk = numberOfAssembliesInApk;
this.uniqueAssemblyNames = uniqueAssemblyNames ?? throw new ArgumentNullException (nameof (uniqueAssemblyNames));
this.marshalMethods = marshalMethods;
this.logger = logger ?? throw new ArgumentNullException (nameof (logger));

generateEmptyCode = false;
defaultCallMarker = LlvmIrCallMarker.Tail;
Expand Down Expand Up @@ -334,7 +334,7 @@ void Init ()

foreach (MarshalMethodInfo method in allMethods) {
if (seenNativeSymbols.Contains (method.NativeSymbolName)) {
logger.LogDebugMessage ($"Removed MM duplicate '{method.NativeSymbolName}' (implemented: {method.Method.ImplementedMethod.FullName}; registered: {method.Method.RegisteredMethod.FullName}");
Log.LogDebugMessage ($"Removed MM duplicate '{method.NativeSymbolName}' (implemented: {method.Method.ImplementedMethod.FullName}; registered: {method.Method.RegisteredMethod.FullName}");
continue;
}

Expand Down
29 changes: 27 additions & 2 deletions src/Xamarin.Android.Build.Tasks/Utilities/NativeTypeMappingData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
using System.Collections.Generic;
using System.Linq;

using Microsoft.Build.Utilities;
using Microsoft.Android.Build.Tasks;

namespace Xamarin.Android.Tasks
{
class NativeTypeMappingData
Expand All @@ -12,7 +15,7 @@ class NativeTypeMappingData
public uint MapModuleCount { get; }
public uint JavaTypeCount { get; }

public NativeTypeMappingData (TypeMapGenerator.ModuleReleaseData[] modules)
public NativeTypeMappingData (TaskLoggingHelper log, TypeMapGenerator.ModuleReleaseData[] modules)
{
Modules = modules ?? throw new ArgumentNullException (nameof (modules));

Expand All @@ -21,15 +24,37 @@ public NativeTypeMappingData (TypeMapGenerator.ModuleReleaseData[] modules)
var tempJavaTypes = new Dictionary<string, TypeMapGenerator.TypeMapReleaseEntry> (StringComparer.Ordinal);
var moduleComparer = new TypeMapGenerator.ModuleUUIDArrayComparer ();

TypeMapGenerator.ModuleReleaseData? monoAndroid = null;
foreach (TypeMapGenerator.ModuleReleaseData data in modules) {
if (data.AssemblyName == "Mono.Android") {
monoAndroid = data;
break;
}
}

if (monoAndroid != null) {
ProcessModule (monoAndroid);
}

foreach (TypeMapGenerator.ModuleReleaseData data in modules) {
if (data.AssemblyName == "Mono.Android") {
continue;
}
ProcessModule (data);
};

void ProcessModule (TypeMapGenerator.ModuleReleaseData data)
{
int moduleIndex = Array.BinarySearch (modules, data, moduleComparer);
if (moduleIndex < 0)
throw new InvalidOperationException ($"Unable to map module with MVID {data.Mvid} to array index");

foreach (TypeMapGenerator.TypeMapReleaseEntry entry in data.Types) {
entry.ModuleIndex = moduleIndex;
if (tempJavaTypes.ContainsKey (entry.JavaName))
if (tempJavaTypes.ContainsKey (entry.JavaName)) {
log.LogDebugMessage ($"Skipping typemap entry for `{entry.ManagedTypeName}, {data.AssemblyName}`; `{entry.JavaName}` is already mapped.");
continue;
}
tempJavaTypes.Add (entry.JavaName, entry);
}
}
Expand Down
16 changes: 9 additions & 7 deletions src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using System.Linq;
using System.Text;

using Microsoft.Build.Utilities;

using Java.Interop.Tools.Cecil;
using Mono.Cecil;
using Microsoft.Android.Build.Tasks;
Expand Down Expand Up @@ -133,17 +135,17 @@ public void AddKnownAssembly (TypeDefinition td)
public string GetAssemblyName (TypeDefinition td) => td.Module.Assembly.FullName;
}

Action<string> logger;
TaskLoggingHelper log;
Encoding outputEncoding;
byte[] moduleMagicString;
byte[] typemapIndexMagicString;
string[] supportedAbis;

public IList<string> GeneratedBinaryTypeMaps { get; } = new List<string> ();

public TypeMapGenerator (Action<string> logger, string[] supportedAbis)
public TypeMapGenerator (TaskLoggingHelper log, string[] supportedAbis)
{
this.logger = logger ?? throw new ArgumentNullException (nameof (logger));
this.log = log ?? throw new ArgumentNullException (nameof (log));
if (supportedAbis == null)
throw new ArgumentNullException (nameof (supportedAbis));
this.supportedAbis = supportedAbis;
Expand Down Expand Up @@ -257,7 +259,7 @@ bool GenerateDebugFiles (bool skipJniAddNativeMethodRegistrationAttributeScan, L
}
GeneratedBinaryTypeMaps.Add (typeMapIndexPath);

var composer = new TypeMappingDebugNativeAssemblyGenerator (new ModuleDebugData ());
var composer = new TypeMappingDebugNativeAssemblyGenerator (log, new ModuleDebugData ());
GenerateNativeAssembly (composer, composer.Construct (), outputDirectory);

return true;
Expand Down Expand Up @@ -289,7 +291,7 @@ bool GenerateDebugNativeAssembly (bool skipJniAddNativeMethodRegistrationAttribu

PrepareDebugMaps (data);

var composer = new TypeMappingDebugNativeAssemblyGenerator (data);
var composer = new TypeMappingDebugNativeAssemblyGenerator (log, data);
GenerateNativeAssembly (composer, composer.Construct (), outputDirectory);

return true;
Expand Down Expand Up @@ -443,7 +445,7 @@ void ProcessReleaseType (ReleaseGenerationState state, TypeDefinition td, Androi
// This is disabled because it costs a lot of time (around 150ms per standard XF Integration app
// build) and has no value for the end user. The message is left here because it may be useful to us
// in our devloop at some point.
//logger ($"Warning: duplicate Java type name '{entry.JavaName}' in assembly '{moduleData.AssemblyName}' (new token: {entry.Token}).");
//log.LogDebugMessage ($"Warning: duplicate Java type name '{entry.JavaName}' in assembly '{moduleData.AssemblyName}' (new token: {entry.Token}).");
moduleData.DuplicateTypes.Add (entry);
} else {
moduleData.TypesScratch.Add (entry.JavaName, entry);
Expand Down Expand Up @@ -482,7 +484,7 @@ bool GenerateRelease (bool skipJniAddNativeMethodRegistrationAttributeScan, List
module.Types = module.TypesScratch.Values.ToArray ();
}

var composer = new TypeMappingReleaseNativeAssemblyGenerator (new NativeTypeMappingData (modules));
var composer = new TypeMappingReleaseNativeAssemblyGenerator (log, new NativeTypeMappingData (log, modules));
GenerateNativeAssembly (arch, composer, composer.Construct (), outputDirectory);
}

Expand Down
Loading

0 comments on commit 25d1f00

Please sign in to comment.