Skip to content

Commit

Permalink
[One .NET] fix more Mono.Android.dll linker warnings (#6270)
Browse files Browse the repository at this point in the history
Context: dotnet/linker#2265
Context: #5652

There are two remaining linker warnings when building with
`$(SuppressTrimAnalysisWarnings)`=false:

	> dotnet build -c Release -p:SuppressTrimAnalysisWarnings=false
	…
	src\Mono.Android\Android.Runtime\ResourceIdManager.cs(37,6): warning IL2026: Android.Runtime.ResourceIdManager.GetResourceTypeFromAssembly(Assembly): Using method 'System.Reflection.Assembly.GetType(String)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Types might be removed.
	src\Mono.Android\Java.Interop\JavaObjectExtensions.cs(136,4): warning IL2026: Java.Interop.JavaObjectExtensions.GetHelperType(Type,String): Using method 'System.Reflection.Assembly.GetType(String)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Types might be removed.
	src\Mono.Android\Java.Interop\JavaObjectExtensions.cs(131,5): warning IL2026: Java.Interop.JavaObjectExtensions.GetHelperType(Type,String): Using method 'System.Reflection.Assembly.GetType(String)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Types might be removed.

The two issues here:

 1. `ResourceIdManager` uses reflection for the implementation of
    `Resource.designer.cs`.  Use `[UnconditionalSuppressMessage]` to
    ignore the warning, because the assembly hitting the warning is
    the "root assembly", which is *not linked*.  The methods in
    question will not be removed by the linker.

 2. `JavaObjectExtensions.GetHelperType()` dynamically looks up
    `*Invoker` types.  Use `[UnconditionalSuppressMessage]` to ignore
    the warning, because the `MarkJavaObjects()` linker step preserves
    them.  Additionally, I renamed the method to `GetInvokerType()`
    and hardcoded `"Invoker"` within the method.  This should prevent
    us from messing this up if a new "helper" type is added in
    addition to `Invoker`.

Unfortunately, even after all the issues are solved. `ILLink` still
appears to emit warnings:

	Java.Interop.dll warning IL2104: Assembly 'Java.Interop' produced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries
	Mono.Android.dll warning IL2104: Assembly 'Mono.Android' produced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries

Adding `-p:TrimmerSingleWarn=false` shows an additional 30 warnings.
  • Loading branch information
jonathanpeppers committed Sep 17, 2021
1 parent 281ced8 commit 141da85
Show file tree
Hide file tree
Showing 5 changed files with 19 additions and 35 deletions.
2 changes: 2 additions & 0 deletions src/Mono.Android/Android.Runtime/ResourceIdManager.cs
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;

namespace Android.Runtime
Expand Down Expand Up @@ -30,6 +31,7 @@ public static void UpdateIdValues ()
}
}

[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Types in Resource.designer.cs are preserved, because it is the root assembly passed to the linker.")]
static Type? GetResourceTypeFromAssembly (Assembly assembly)
{
foreach (var customAttribute in assembly.GetCustomAttributes (typeof (ResourceDesignerAttribute), true)) {
Expand Down
10 changes: 6 additions & 4 deletions src/Mono.Android/Java.Interop/JavaObjectExtensions.cs
Expand Up @@ -92,7 +92,7 @@ static IJavaObject CastClass (IJavaObject instance, Type resultType)

if (resultType.IsAbstract) {
// TODO: keep in sync with TypeManager.CreateInstance() algorithm
var invokerType = GetHelperType (resultType, "Invoker");
var invokerType = GetInvokerType (resultType);
if (invokerType == null)
throw new ArgumentException ("Unable to get Invoker for abstract type '" + resultType.FullName + "'.", "TResult");
resultType = invokerType;
Expand Down Expand Up @@ -122,10 +122,12 @@ static IJavaObject CastClass (IJavaObject instance, Type resultType)
instance.GetType ().FullName, resultType.FullName));
}

// typeof(Foo) -> FooSuffix
// typeof(Foo<>) -> FooSuffix`1
internal static Type? GetHelperType (Type type, string suffix)
// typeof(Foo) -> FooInvoker
// typeof(Foo<>) -> FooInvoker`1
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "*Invoker types are preserved by the MarkJavaObjects linker step.")]
internal static Type? GetInvokerType (Type type)
{
const string suffix = "Invoker";
Type[] arguments = type.GetGenericArguments ();
if (arguments.Length == 0)
return type.Assembly.GetType (type + suffix);
Expand Down
2 changes: 1 addition & 1 deletion src/Mono.Android/Java.Interop/TypeManager.cs
Expand Up @@ -292,7 +292,7 @@ internal static IJavaPeerable CreateInstance (IntPtr handle, JniHandleOwnership
type = targetType;

if (type.IsInterface || type.IsAbstract) {
var invokerType = JavaObjectExtensions.GetHelperType (type, "Invoker");
var invokerType = JavaObjectExtensions.GetInvokerType (type);
if (invokerType == null)
throw new NotSupportedException ("Unable to find Invoker for type '" + type.FullName + "'. Was it linked away?",
CreateJavaLocationException ());
Expand Down
Expand Up @@ -59,7 +59,6 @@ void PreserveJavaObjectImplementation (TypeDefinition type)
{
PreserveIntPtrConstructor (type);
PreserveAttributeSetConstructor (type);
PreserveAdapter (type);
PreserveInvoker (type);
}

Expand Down Expand Up @@ -151,24 +150,6 @@ void PreserveMethod (TypeDefinition type, MethodDefinition method)
Annotations.AddPreservedMethod (type, method);
}

void PreserveAdapter (TypeDefinition type)
{
var adapter = PreserveHelperType (type, "Adapter");

if (adapter == null || !adapter.HasMethods)
return;

foreach (MethodDefinition method in adapter.Methods) {
if (method.Name != "GetObject")
continue;

if (method.Parameters.Count != 2)
continue;

PreserveMethod (type, method);
}
}

string TypeNameWithoutKey (string name)
{
var idx = name.IndexOf (", PublicKeyToken=");
Expand Down Expand Up @@ -208,25 +189,18 @@ void PreserveInterfaceMethods (TypeDefinition type, TypeDefinition invoker)

void PreserveInvoker (TypeDefinition type)
{
var invoker = PreserveHelperType (type, "Invoker");
var invoker = GetInvokerType (type);
if (invoker == null)
return;

PreserveConstructors (type, invoker);
PreserveIntPtrConstructor (invoker);
PreserveInterfaceMethods (type, invoker);
}

TypeDefinition PreserveHelperType (TypeDefinition type, string suffix)
{
var helper = GetHelperType (type, suffix);
if (helper != null)
PreserveConstructors (type, helper);

return helper;
}

TypeDefinition GetHelperType (TypeDefinition type, string suffix)
TypeDefinition GetInvokerType (TypeDefinition type)
{
const string suffix = "Invoker";
string fullname = type.FullName;

if (type.HasGenericParameters) {
Expand Down
Expand Up @@ -205,8 +205,14 @@ public void DotNetNew ([Values ("android", "androidlib", "android-bindinglib")]
File.WriteAllBytes (Path.Combine (dotnet.ProjectDirectory, "foo.jar"), ResourceData.JavaSourceJarTestJar);
Assert.IsTrue (dotnet.New ("android-activity"), "`dotnet new android-activity` should succeed");
Assert.IsTrue (dotnet.New ("android-layout", Path.Combine (dotnet.ProjectDirectory, "Resources", "layout")), "`dotnet new android-layout` should succeed");

// Debug build
Assert.IsTrue (dotnet.Build (), "`dotnet build` should succeed");
dotnet.AssertHasNoWarnings ();

// Release build
Assert.IsTrue (dotnet.Build (parameters: new [] { "Configuration=Release" }), "`dotnet build` should succeed");
dotnet.AssertHasNoWarnings ();
}

[Test]
Expand Down

0 comments on commit 141da85

Please sign in to comment.