Skip to content

Commit

Permalink
[trimming] fix custom applications for TrimMode=full (#8936)
Browse files Browse the repository at this point in the history
Fixes: #8914

Using a custom application with `TrimMode=full`:

    [Application]
    public class CustomApp : Application
    {
        public CustomApp(IntPtr h, JniHandleOwnership o) : base(h, o) { }
    }

Fails with:

    ILLink : error IL1012: IL Trimmer has encountered an unexpected error. Please report the issue at https://aka.ms/report-illink
    Fatal error in IL Linker
    Unhandled exception. System.InvalidOperationException: Sequence contains no matching element
        at System.Linq.ThrowHelper.ThrowNoMatchException()
        at System.Linq.Enumerable.First[TSource](IEnumerable`1 source, Func`2 predicate)
        at MonoDroid.Tuner.PreserveApplications.PreserveTypeProperty(CustomAttribute attribute, String property) in /Users/runner/work/1/s/xamarin-android/src/Microsoft.Android.Sdk.ILLink/PreserveApplications.cs:line 69
        at MonoDroid.Tuner.PreserveApplications.PreserveApplicationAttribute(CustomAttribute attribute) in /Users/runner/work/1/s/xamarin-android/src/Microsoft.Android.Sdk.ILLink/PreserveApplications.cs:line 60
        at MonoDroid.Tuner.PreserveApplications.ProcessAttributeProvider(ICustomAttributeProvider provider) in /Users/runner/work/1/s/xamarin-android/src/Microsoft.Android.Sdk.ILLink/PreserveApplications.cs:line 55
        at MonoDroid.Tuner.PreserveApplications.ProcessType(TypeDefinition type) in /Users/runner/work/1/s/xamarin-android/src/Microsoft.Android.Sdk.ILLink/PreserveApplications.cs:line 43
        at MonoDroid.Tuner.PreserveApplications.<Initialize>b__0_1(TypeDefinition type) in /Users/runner/work/1/s/xamarin-android/src/Microsoft.Android.Sdk.ILLink/PreserveApplications.cs:line 21
        at Mono.Linker.Steps.MarkStep.MarkType(TypeReference reference, DependencyInfo reason, Nullable`1 origin)
        at Mono.Linker.Steps.MarkStep.MarkTypeVisibleToReflection(TypeReference type, TypeDefinition definition, DependencyInfo& reason, MessageOrigin& origin)
        at Mono.Linker.Steps.MarkStep.MarkEntireType(TypeDefinition type, DependencyInfo& reason)
        at Mono.Linker.Steps.MarkStep.MarkEntireAssembly(AssemblyDefinition assembly)
        at Mono.Linker.Steps.MarkStep.MarkAssembly(AssemblyDefinition assembly, DependencyInfo reason)
        at Mono.Linker.Steps.MarkStep.MarkModule(ModuleDefinition module, DependencyInfo reason)
        at Mono.Linker.Steps.MarkStep.ProcessMarkedPending()
        at Mono.Linker.Steps.MarkStep.Initialize()
        at Mono.Linker.Steps.MarkStep.Process(LinkContext context)
        at Mono.Linker.Pipeline.ProcessStep(LinkContext context, IStep step)
        at Mono.Linker.Pipeline.Process(LinkContext context)
        at Mono.Linker.Driver.Run(ILogger customLogger)
        at Mono.Linker.Driver.Main(String[] args)
        at Mono.Linker.Driver.Main(String[] args)
    c:\Nuget\microsoft.net.illink.tasks\9.0.0-preview.3.24172.9\build\Microsoft.NET.ILLink.targets(91,5): error NETSDK1144: Optimizing assemblies for size failed.

I could update an existing `CustomApplicationClassAndMultiDex` test
to reproduce the problem.

What is happening is:

* `TrimMode=full` makes the trimmer way more aggressive.

* `Android.App.ApplicationAttribute` has various unused members trimmed
  away.

* `BackupAgent` and `ManageSpaceActivity` properties are trimmed away.

* Custom trimmer step throws an exception when trying to access these
  trimmed properties.

The trimmer step *already* handles cases where it can't load the types
and happily continues on. We should just do the same if the properties
are not found: as it would mean a customer didn't set them.

As a workaround, @tranb3r tried:

    [global::Android.App.ApplicationAttribute(
        ...
        BackupAgent = null,
        ManageSpaceActivity = null
    )]

But this results in a `NullReferenceException` in `ManifestDocumentElement`:

    C:\Program Files\dotnet\packs\Microsoft.Android.Sdk.Windows\34.99.0-preview.3.231\tools\Xamarin.Android.Common.targets(1460,3): error XAGJS7001: System.NullReferenceException: Object reference not set to an instance of an object.
    at Xamarin.Android.Manifest.ManifestDocumentElement.ResolveType(String type, ICustomAttributeProvider provider, IAssemblyResolver resolver)
    at Android.App.ApplicationAttribute.<>c.<.cctor>b__114_7(ApplicationAttribute self, ICustomAttributeProvider p, IAssemblyResolver r, TypeDefinitionCache cache)
    at Xamarin.Android.Manifest.ManifestDocumentElement`1.ToAttributeValue(String name, T value, ICustomAttributeProvider provider, IAssemblyResolver resolver, TypeDefinitionCache cache, Int32 targetSdkVersion)
    at Xamarin.Android.Manifest.ManifestDocumentElement`1.ToAttribute(String name, T value, String packageName, ICustomAttributeProvider provider, IAssemblyResolver resolver, TypeDefinitionCache cache, Int32 targetSdkVersion)
    at Xamarin.Android.Manifest.ManifestDocumentElement`1.<>c__DisplayClass8_0.<ToElement>b__1(String e)
    at System.Linq.Enumerable.IteratorSelectIterator`2.MoveNext()
    at System.Linq.Enumerable.IEnumerableWhereIterator`1.MoveNext()
    at System.Xml.Linq.XContainer.AddContentSkipNotify(Object content)
    at Xamarin.Android.Manifest.ManifestDocumentElement`1.ToElement(T value, ICollection`1 specified, String packageName, TypeDefinitionCache cache, ICustomAttributeProvider provider, IAssemblyResolver resolver, Int32 targetSdkVersion)
    at Android.App.ApplicationAttribute.ToElement(IAssemblyResolver resolver, String packageName, TypeDefinitionCache cache)
    at Xamarin.Android.Tasks.ManifestDocument.CreateApplicationElement(XElement manifest, String applicationClass, List`1 subclasses, TypeDefinitionCache cache)
    at Xamarin.Android.Tasks.ManifestDocument.Merge(TaskLoggingHelper log, TypeDefinitionCache cache, List`1 subclasses, String applicationClass, Boolean embed, String bundledWearApplicationName, IEnumerable`1 mergedManifestDocuments)
    at Xamarin.Android.Tasks.GenerateJavaStubs.MergeManifest(NativeCodeGenState codeGenState, Dictionary`2 userAssemblies)
    at Xamarin.Android.Tasks.GenerateJavaStubs.Run(Boolean useMarshalMethods)
    at Xamarin.Android.Tasks.GenerateJavaStubs.RunTask()
    at Microsoft.Android.Build.Tasks.AndroidTask.Execute() in /Users/runner/work/1/s/xamarin-android/external/xamarin-android-tools/src/Microsoft.Android.Build.BaseTasks/AndroidTask.cs:line 25

These does not appear to be valid settings *anyway*, but I updated the
code so it would throw a more appropriate `ArgumentException` instead
of `NullReferenceException` in a random location inside the method.
  • Loading branch information
jonathanpeppers committed May 13, 2024
1 parent 2b6fcfc commit 98c1063
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 5 deletions.
7 changes: 6 additions & 1 deletion src/Microsoft.Android.Sdk.ILLink/PreserveApplications.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,12 @@ void PreserveTypeProperty (CustomAttribute attribute, string property)
if (!attribute.HasProperties)
return;

var type_ref = (TypeReference) attribute.Properties.First (p => p.Name == property).Argument.Value;
// NOTE: CustomAttributeNamedArgument is a struct
var named_arg = attribute.Properties.FirstOrDefault (p => p.Name == property);
if (named_arg.Name == null)
return;

var type_ref = named_arg.Argument.Value as TypeReference;
if (type_ref == null)
return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1083,9 +1083,11 @@ public void BuildAfterMultiDexIsNotRequired ()
}

[Test]
public void CustomApplicationClassAndMultiDex ()
public void CustomApplicationClassAndMultiDex ([Values (true, false)] bool isRelease)
{
var proj = CreateMultiDexRequiredApplication ();
proj.IsRelease = isRelease;
proj.TrimModeRelease = TrimMode.Full;
proj.SetProperty ("AndroidEnableMultiDex", "True");
proj.Sources.Add (new BuildItem ("Compile", "CustomApp.cs") { TextContent = () => @"
using System;
Expand All @@ -1108,7 +1110,7 @@ public override void OnCreate()
}
}
}" });
using (var b = CreateApkBuilder ("temp/CustomApplicationClassAndMultiDex")) {
using (var b = CreateApkBuilder ()) {
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
Assert.IsFalse (b.LastBuildOutput.ContainsText ("Duplicate zip entry"), "Should not get warning about [META-INF/MANIFEST.MF]");
var customAppContent = File.ReadAllText (Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "src", "com", "foxsports", "test", "CustomApp.java"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ public static string ToString (TypeDefinition typeDef, TypeDefinitionCache cache

public static TypeDefinition ResolveType (string type, ICustomAttributeProvider provider, IAssemblyResolver resolver)
{
if (type == null)
throw new ArgumentException ("Type resolution support requires a non-null Type.", nameof (type));
if (provider == null)
throw new ArgumentException ("Type resolution support requires an AssemblyDefinition or TypeDefinition.", "provider");
throw new ArgumentException ("Type resolution support requires an AssemblyDefinition or TypeDefinition.", nameof (provider));
if (resolver == null)
throw new ArgumentException ("Type resolution support requires a IAssemblyResolver.", "resolver");
throw new ArgumentException ("Type resolution support requires a IAssemblyResolver.", nameof (resolver));

// `type` is either a "bare" type "Foo.Bar", or an
// assembly-qualified type "Foo.Bar, AssemblyName [Version=...]?".
Expand Down

0 comments on commit 98c1063

Please sign in to comment.