diff --git a/src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj b/src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj index 92c92c0ee94..c92b0e32b1a 100644 --- a/src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj +++ b/src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj @@ -16,7 +16,6 @@ - diff --git a/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml b/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml index aa0a341e0bb..124edd61d93 100644 --- a/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml +++ b/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml @@ -34,6 +34,9 @@ --> + + + diff --git a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixAbstractMethodsStep.cs b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixAbstractMethodsStep.cs index 467533a0a59..c1ced554bb1 100644 --- a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixAbstractMethodsStep.cs +++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixAbstractMethodsStep.cs @@ -10,68 +10,43 @@ using Mono.Linker.Steps; using Mono.Tuner; using Xamarin.Android.Tasks; - -#if ILLINK -using Resources = Microsoft.Android.Sdk.ILLink.Properties.Resources; -#else // !ILLINK using Resources = Xamarin.Android.Tasks.Properties.Resources; -#endif // !ILLINK namespace MonoDroid.Tuner { - /// - /// NOTE: this step is subclassed so it can be called directly from Xamarin.Android.Build.Tasks - /// - public class FixAbstractMethodsStep : BaseMarkHandler -#if !ILLINK - , IAssemblyModifierPipelineStep -#endif // !ILLINK + public class FixAbstractMethodsStep : BaseStep, IAssemblyModifierPipelineStep { - public override void Initialize (LinkContext context, MarkContext markContext) + readonly IMetadataResolver _injectedCache; + readonly Func _injectedGetMonoAndroidAssembly; + readonly Action _injectedLogMessage; + + /// + /// Used by LinkAssembliesNoShrink and tests. Call before use. + /// + public FixAbstractMethodsStep () { } + + /// + /// Used by when no is available. + /// + internal FixAbstractMethodsStep ( + IMetadataResolver cache, + Func getMonoAndroidAssembly, + Action logMessage) { - base.Initialize (context, markContext); - markContext.RegisterMarkTypeAction (type => ProcessType (type)); + _injectedCache = cache; + _injectedGetMonoAndroidAssembly = getMonoAndroidAssembly; + _injectedLogMessage = logMessage; } - bool CheckShouldProcessAssembly (AssemblyDefinition assembly) - { - if (!Annotations.HasAction (assembly)) - Annotations.SetAction (assembly, AssemblyAction.Skip); - - if (MonoAndroidHelper.IsFrameworkAssembly (assembly)) - return false; - - CheckAppDomainUsage (assembly, (string msg) => -#if ILLINK - Context.LogMessage (MessageContainer.CreateCustomWarningMessage (Context, msg, 6200, new MessageOrigin (), WarnVersion.ILLink5)) -#else // !ILLINK - Context.LogWarning ("XA2000", msg) -#endif // !ILLINK - ); + IMetadataResolver TypeCache => _injectedCache ?? Context; - return assembly.MainModule.HasTypeReference ("Java.Lang.Object"); - } - - void UpdateAssemblyAction (AssemblyDefinition assembly) - { - if (Annotations.GetAction (assembly) == AssemblyAction.Copy) - Annotations.SetAction (assembly, AssemblyAction.Save); - } - - void ProcessType (TypeDefinition type) + public void ProcessAssembly (AssemblyDefinition assembly, StepContext context) { - var assembly = type.Module.Assembly; - if (!CheckShouldProcessAssembly (assembly)) - return; - - if (!MightNeedFix (type)) - return; - - if (!FixAbstractMethods (type)) + // Only run this step on non-main user Android assemblies + if (context.IsMainAssembly || !context.IsAndroidUserAssembly) return; - UpdateAssemblyAction (assembly); - MarkAbstractMethodErrorType (); + context.IsAssemblyModified |= FixAbstractMethods (assembly); } internal bool FixAbstractMethods (AssemblyDefinition assembly) @@ -84,17 +59,6 @@ internal bool FixAbstractMethods (AssemblyDefinition assembly) return changed; } -#if !ILLINK - public void ProcessAssembly (AssemblyDefinition assembly, StepContext context) - { - // Only run this step on non-main user Android assemblies - if (context.IsMainAssembly || !context.IsAndroidUserAssembly) - return; - - context.IsAssemblyModified |= FixAbstractMethods (assembly); - } -#endif // !ILLINK - readonly HashSet warnedAssemblies = new (StringComparer.Ordinal); internal void CheckAppDomainUsage (AssemblyDefinition assembly, Action warn) @@ -114,7 +78,7 @@ internal void CheckAppDomainUsage (AssemblyDefinition assembly, Action w bool MightNeedFix (TypeDefinition type) { - return !type.IsAbstract && type.IsSubclassOf ("Java.Lang.Object", cache); + return !type.IsAbstract && type.IsSubclassOf ("Java.Lang.Object", TypeCache); } bool CompareTypes (TypeReference iType, TypeReference tType) @@ -140,11 +104,11 @@ bool CompareTypes (TypeReference iType, TypeReference tType) if (iType.Namespace != tType.Namespace) return false; - TypeDefinition iTypeDef = cache.Resolve (iType); + TypeDefinition iTypeDef = TypeCache.Resolve (iType); if (iTypeDef == null) return false; - TypeDefinition tTypeDef = cache.Resolve (tType); + TypeDefinition tTypeDef = TypeCache.Resolve (tType); if (tTypeDef == null) return false; @@ -174,7 +138,7 @@ bool IsInOverrides (MethodDefinition iMethod, MethodDefinition tMethod) return false; foreach (var o in tMethod.Overrides) - if (o != null && iMethod.Name == o.Name && iMethod == cache.Resolve (o)) + if (o != null && iMethod.Name == o.Name && iMethod == TypeCache.Resolve (o)) return true; return false; @@ -223,12 +187,12 @@ bool FixAbstractMethods (TypeDefinition type) bool rv = false; List typeMethods = new List (type.Methods); - foreach (var baseType in type.GetBaseTypes (cache)) + foreach (var baseType in type.GetBaseTypes (TypeCache)) typeMethods.AddRange (baseType.Methods); foreach (var ifaceInfo in type.Interfaces) { var iface = ifaceInfo.InterfaceType; - var ifaceDef = cache.Resolve (iface); + var ifaceDef = TypeCache.Resolve (iface); if (ifaceDef == null) { LogMessage ($"Unable to unresolve interface: {iface.FullName}"); continue; @@ -308,33 +272,20 @@ MethodDefinition AbstractMethodErrorConstructor { } } - bool markedAbstractMethodErrorType; - - void MarkAbstractMethodErrorType () + public override void LogMessage (string message) { - if (markedAbstractMethodErrorType) + if (_injectedLogMessage != null) { + _injectedLogMessage (message); return; - markedAbstractMethodErrorType = true; - - - var td = AbstractMethodErrorConstructor.DeclaringType; - Annotations.Mark (td); - Annotations.SetPreserve (td, TypePreserve.Nothing); - Annotations.AddPreservedMethod (td, AbstractMethodErrorConstructor); - } - - public virtual void LogMessage (string message) - { - Context.LogMessage (message); + } + base.LogMessage (message); } AssemblyDefinition GetMonoAndroidAssembly () { -#if !ILLINK + if (_injectedGetMonoAndroidAssembly != null) + return _injectedGetMonoAndroidAssembly (); return Context.Resolver.GetAssembly ("Mono.Android.dll"); -#else // ILLINK - return Context.GetLoadedAssembly ("Mono.Android"); -#endif // ILLINK } } } diff --git a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/PostTrimmingFixAbstractMethodsStep.cs b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/PostTrimmingFixAbstractMethodsStep.cs new file mode 100644 index 00000000000..dbf3f3853e4 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/PostTrimmingFixAbstractMethodsStep.cs @@ -0,0 +1,44 @@ +#nullable enable + +using System; +using Java.Interop.Tools.Cecil; +using Microsoft.Android.Build.Tasks; +using Mono.Cecil; +using Xamarin.Android.Tasks; + +namespace MonoDroid.Tuner; + +/// +/// Post-trimming version of FixAbstractMethodsStep that delegates to the core logic +/// in . Skips framework assemblies, checks for +/// AppDomain.CreateDomain usage, and fixes missing abstract method implementations +/// on Java.Lang.Object subclasses. +/// +class PostTrimmingFixAbstractMethodsStep : IAssemblyModifierPipelineStep +{ + readonly FixAbstractMethodsStep _step; + readonly Action _warn; + + public PostTrimmingFixAbstractMethodsStep ( + IMetadataResolver cache, + Func getMonoAndroidAssembly, + Action logMessage, + Action warn) + { + _step = new FixAbstractMethodsStep (cache, getMonoAndroidAssembly, logMessage); + _warn = warn; + } + + public void ProcessAssembly (AssemblyDefinition assembly, StepContext context) + { + if (MonoAndroidHelper.IsFrameworkAssembly (assembly)) + return; + + _step.CheckAppDomainUsage (assembly, _warn); + + if (!assembly.MainModule.HasTypeReference ("Java.Lang.Object")) + return; + + context.IsAssemblyModified |= _step.FixAbstractMethods (assembly); + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets index 84e68d8bf6f..f7706bd4be0 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets @@ -195,7 +195,6 @@ <_TrimmerCustomSteps Include="$(_AndroidLinkerCustomStepAssembly)" Type="MonoDroid.Tuner.PreserveApplications" /> <_TrimmerCustomSteps Include="$(_AndroidLinkerCustomStepAssembly)" Type="Microsoft.Android.Sdk.ILLink.PreserveRegistrations" /> <_TrimmerCustomSteps Include="$(_AndroidLinkerCustomStepAssembly)" Type="Microsoft.Android.Sdk.ILLink.PreserveJavaInterfaces" /> - <_TrimmerCustomSteps Include="$(_AndroidLinkerCustomStepAssembly)" Type="MonoDroid.Tuner.FixAbstractMethodsStep" /> <_TrimmerCustomSteps diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssembliesNoShrink.cs b/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssembliesNoShrink.cs index 9dc7e99e635..cde9d9bc8f9 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssembliesNoShrink.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssembliesNoShrink.cs @@ -1,7 +1,6 @@ #nullable enable using System; -using Mono.Linker.Steps; using MonoDroid.Tuner; namespace Xamarin.Android.Tasks @@ -22,7 +21,7 @@ protected override void BuildPipeline (AssemblyPipeline pipeline, MSBuildLinkCon { // FixAbstractMethodsStep var fixAbstractMethodsStep = new FixAbstractMethodsStep (); - fixAbstractMethodsStep.Initialize (context, new EmptyMarkContext ()); + fixAbstractMethodsStep.Initialize (context); pipeline.Steps.Add (fixAbstractMethodsStep); // FixLegacyResourceDesignerStep diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs b/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs index 7a33ea3c7be..8c93973bd4e 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs @@ -47,6 +47,22 @@ public override bool RunTask () var steps = new List (); steps.Add (new StripEmbeddedLibrariesStep (Log)); + + // FixAbstractMethods — resolve Mono.Android once up front. If resolution fails, log + // the error and skip running the fix step entirely to avoid later unhandled exceptions. + AssemblyDefinition? monoAndroidAssembly = null; + try { + monoAndroidAssembly = resolver.Resolve (AssemblyNameReference.Parse ("Mono.Android")); + } catch (AssemblyResolutionException ex) { + Log.LogErrorFromException (ex, showStackTrace: false); + } + if (monoAndroidAssembly != null) { + steps.Add (new PostTrimmingFixAbstractMethodsStep (cache, + () => monoAndroidAssembly, + (msg) => Log.LogDebugMessage (msg), + (msg) => Log.LogCodedWarning ("XA2000", msg))); + } + if (AddKeepAlives) { // Memoize the corlib resolution so the attempt (and any error logging) happens at most once, // regardless of how many assemblies/methods need KeepAlive injection. diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs index 1c995304248..ca87f1367bc 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs @@ -8,7 +8,6 @@ using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Linker; -using Mono.Linker.Steps; using Mono.Tuner; using MonoDroid.Tuner; using NUnit.Framework; @@ -33,7 +32,7 @@ public void FixAbstractMethodsStep_SkipDimMembers () using (var resolver = new DirectoryAssemblyResolver (Logger, false)) using (var context = new LinkContext (resolver)) { - step.Initialize (context, new EmptyMarkContext ()); + step.Initialize (context); resolver.SearchDirectories.Add (path); var myAssemblyPath = Path.Combine (path, "MyAssembly.dll"); @@ -92,7 +91,7 @@ public void FixAbstractMethodsStep_Explicit () using (var resolver = new DirectoryAssemblyResolver (Logger, false)) using (var context = new LinkContext (resolver)) { - step.Initialize (context, new EmptyMarkContext ()); + step.Initialize (context); resolver.SearchDirectories.Add (path); var myAssemblyPath = Path.Combine (path, "MyAssembly.dll"); diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj index c9157be3712..d6b665810ce 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj @@ -56,6 +56,7 @@ +