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/FixAbstractMethodsHelper.cs b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixAbstractMethodsHelper.cs
new file mode 100644
index 00000000000..599728580c6
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixAbstractMethodsHelper.cs
@@ -0,0 +1,223 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using Java.Interop.Tools.Cecil;
+using Mono.Cecil;
+
+using Resources = Xamarin.Android.Tasks.Properties.Resources;
+
+namespace MonoDroid.Tuner
+{
+ static class FixAbstractMethodsHelper
+ {
+ internal static bool FixAbstractMethods (AssemblyDefinition assembly, IMetadataResolver cache, ref MethodDefinition? abstractMethodErrorCtor, Func getMonoAndroidAssembly, Action logMessage)
+ {
+ bool changed = false;
+ foreach (var type in assembly.MainModule.Types) {
+ if (MightNeedFix (type, cache))
+ changed |= FixAbstractMethods (type, cache, ref abstractMethodErrorCtor, getMonoAndroidAssembly, logMessage);
+ }
+ return changed;
+ }
+
+ internal static void CheckAppDomainUsage (AssemblyDefinition assembly, Action warn, HashSet warnedAssemblies)
+ {
+ if (!warnedAssemblies.Add (assembly.Name.Name))
+ return;
+ if (!assembly.MainModule.HasTypeReference ("System.AppDomain"))
+ return;
+
+ foreach (var mr in assembly.MainModule.GetMemberReferences ()) {
+ if (mr.ToString ().Contains ("System.AppDomain System.AppDomain::CreateDomain")) {
+ warn (string.Format (CultureInfo.CurrentCulture, Resources.XA2000, assembly));
+ break;
+ }
+ }
+ }
+
+ static bool MightNeedFix (TypeDefinition type, IMetadataResolver cache)
+ {
+ return !type.IsAbstract && type.IsSubclassOf ("Java.Lang.Object", cache);
+ }
+
+ static bool CompareTypes (TypeReference iType, TypeReference tType, IMetadataResolver cache)
+ {
+ if (iType.IsGenericParameter)
+ return true;
+
+ if (iType.IsArray) {
+ if (!tType.IsArray)
+ return false;
+ return CompareTypes (iType.GetElementType (), tType.GetElementType (), cache);
+ }
+
+ if (iType.IsByReference) {
+ if (!tType.IsByReference)
+ return false;
+ return CompareTypes (iType.GetElementType (), tType.GetElementType (), cache);
+ }
+
+ if (iType.Name != tType.Name)
+ return false;
+
+ if (iType.Namespace != tType.Namespace)
+ return false;
+
+ var iTypeDef = cache.Resolve (iType);
+ if (iTypeDef == null)
+ return false;
+
+ var tTypeDef = cache.Resolve (tType);
+ if (tTypeDef == null)
+ return false;
+
+ if (iTypeDef.Module.FileName != tTypeDef.Module.FileName)
+ return false;
+
+ if (iType is GenericInstanceType iGType && tType is GenericInstanceType tGType) {
+ if (iGType.GenericArguments.Count != tGType.GenericArguments.Count)
+ return false;
+ for (int i = 0; i < iGType.GenericArguments.Count; i++) {
+ if (iGType.GenericArguments [i].IsGenericParameter)
+ continue;
+ if (!CompareTypes (iGType.GenericArguments [i], tGType.GenericArguments [i], cache))
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ static bool IsInOverrides (MethodDefinition iMethod, MethodDefinition tMethod, IMetadataResolver cache)
+ {
+ if (!tMethod.HasOverrides)
+ return false;
+
+ foreach (var o in tMethod.Overrides)
+ if (o != null && iMethod.Name == o.Name && iMethod == cache.Resolve (o))
+ return true;
+
+ return false;
+ }
+
+ static bool HaveSameSignature (TypeReference iface, MethodDefinition iMethod, MethodDefinition tMethod, IMetadataResolver cache)
+ {
+ if (IsInOverrides (iMethod, tMethod, cache))
+ return true;
+
+ if (iMethod.Name != tMethod.Name)
+ return false;
+
+ if (!CompareTypes (iMethod.MethodReturnType.ReturnType, tMethod.MethodReturnType.ReturnType, cache))
+ return false;
+
+ if (iMethod.Parameters.Count != tMethod.Parameters.Count || iMethod.GenericParameters.Count != tMethod.GenericParameters.Count)
+ return false;
+
+ if (iMethod.HasParameters) {
+ List m1p = new List (iMethod.Parameters);
+ List m2p = new List (tMethod.Parameters);
+
+ for (int i = 0; i < m1p.Count; i++) {
+ if (!CompareTypes (m1p [i].ParameterType, m2p [i].ParameterType, cache))
+ return false;
+ }
+ }
+
+ if (iMethod.HasGenericParameters) {
+ List m1p = new List (iMethod.GenericParameters);
+ List m2p = new List (tMethod.GenericParameters);
+
+ for (int i = 0; i < m1p.Count; i++)
+ if (!CompareTypes (m1p [i], m2p [i], cache))
+ return false;
+ }
+
+ return true;
+ }
+
+ static bool FixAbstractMethods (TypeDefinition type, IMetadataResolver cache, ref MethodDefinition? abstractMethodErrorCtor, Func getMonoAndroidAssembly, Action logMessage)
+ {
+ if (!type.HasInterfaces)
+ return false;
+
+ bool rv = false;
+ List typeMethods = new List (type.Methods);
+ foreach (var baseType in type.GetBaseTypes (cache))
+ typeMethods.AddRange (baseType.Methods);
+
+ foreach (var ifaceInfo in type.Interfaces) {
+ var iface = ifaceInfo.InterfaceType;
+ var ifaceDef = cache.Resolve (iface);
+ if (ifaceDef == null) {
+ logMessage ($"Unable to unresolve interface: {iface.FullName}");
+ continue;
+ }
+ if (ifaceDef.HasGenericParameters)
+ continue;
+
+ foreach (var iMethod in ifaceDef.Methods.Where (m => m.IsAbstract)) {
+ bool exists = false;
+
+ foreach (var tMethod in typeMethods) {
+ if (HaveSameSignature (iface, iMethod, tMethod, cache)) {
+ exists = true;
+ break;
+ }
+ }
+
+ if (!exists) {
+ abstractMethodErrorCtor ??= GetAbstractMethodErrorConstructor (getMonoAndroidAssembly);
+ AddNewExceptionMethod (type, iMethod, abstractMethodErrorCtor, logMessage);
+ rv = true;
+ }
+ }
+ }
+
+ return rv;
+ }
+
+ static TypeReference TryImportType (TypeDefinition declaringType, TypeReference type)
+ {
+ if (type.IsGenericParameter)
+ return type;
+
+ return declaringType.Module.ImportReference (type);
+ }
+
+ static void AddNewExceptionMethod (TypeDefinition type, MethodDefinition method, MethodDefinition abstractMethodErrorCtor, Action logMessage)
+ {
+ var newMethod = new MethodDefinition (method.Name, (method.Attributes | MethodAttributes.Final) & ~MethodAttributes.Abstract, TryImportType (type, method.ReturnType));
+
+ foreach (var paramater in method.Parameters)
+ newMethod.Parameters.Add (new ParameterDefinition (paramater.Name, paramater.Attributes, TryImportType (type, paramater.ParameterType)));
+
+ var ilP = newMethod.Body.GetILProcessor ();
+
+ ilP.Append (ilP.Create (Mono.Cecil.Cil.OpCodes.Newobj, type.Module.ImportReference (abstractMethodErrorCtor)));
+ ilP.Append (ilP.Create (Mono.Cecil.Cil.OpCodes.Throw));
+
+ type.Methods.Add (newMethod);
+
+ logMessage ($"Added method: {method} to type: {type.FullName} scope: {type.Scope}");
+ }
+
+ static MethodDefinition GetAbstractMethodErrorConstructor (Func getMonoAndroidAssembly)
+ {
+ var assembly = getMonoAndroidAssembly ();
+ if (assembly != null) {
+ var errorException = assembly.MainModule.GetType ("Java.Lang.AbstractMethodError");
+ if (errorException != null) {
+ foreach (var method in errorException.Methods) {
+ if (method.Name == ".ctor" && !method.HasParameters) {
+ return method;
+ }
+ }
+ }
+ }
+
+ throw new Exception ("Unable to find Java.Lang.AbstractMethodError constructor in Mono.Android assembly");
+ }
+ }
+}
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..ba7b64602aa 100644
--- a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixAbstractMethodsStep.cs
+++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixAbstractMethodsStep.cs
@@ -1,340 +1,29 @@
-#nullable disable
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using Java.Interop.Tools.Cecil;
using Mono.Cecil;
-using Mono.Linker;
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
+ /// Simplified FixAbstractMethodsStep for the no-trim path (LinkAssembliesNoShrink).
+ /// Core logic is in .
///
- public class FixAbstractMethodsStep : BaseMarkHandler
-#if !ILLINK
- , IAssemblyModifierPipelineStep
-#endif // !ILLINK
+ public class FixAbstractMethodsStep : BaseStep, IAssemblyModifierPipelineStep
{
- public override void Initialize (LinkContext context, MarkContext markContext)
- {
- base.Initialize (context, markContext);
- markContext.RegisterMarkTypeAction (type => ProcessType (type));
- }
-
- 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
- );
-
- return assembly.MainModule.HasTypeReference ("Java.Lang.Object");
- }
-
- void UpdateAssemblyAction (AssemblyDefinition assembly)
- {
- if (Annotations.GetAction (assembly) == AssemblyAction.Copy)
- Annotations.SetAction (assembly, AssemblyAction.Save);
- }
+ MethodDefinition? abstractMethodErrorCtor;
- void ProcessType (TypeDefinition type)
- {
- var assembly = type.Module.Assembly;
- if (!CheckShouldProcessAssembly (assembly))
- return;
-
- if (!MightNeedFix (type))
- return;
-
- if (!FixAbstractMethods (type))
- return;
-
- UpdateAssemblyAction (assembly);
- MarkAbstractMethodErrorType ();
- }
-
- internal bool FixAbstractMethods (AssemblyDefinition assembly)
- {
- bool changed = false;
- foreach (var type in assembly.MainModule.Types) {
- if (MightNeedFix (type))
- changed |= FixAbstractMethods (type);
- }
- 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)
- {
- if (!warnedAssemblies.Add (assembly.Name.Name))
- return;
- if (!assembly.MainModule.HasTypeReference ("System.AppDomain"))
- return;
-
- foreach (var mr in assembly.MainModule.GetMemberReferences ()) {
- if (mr.ToString ().Contains ("System.AppDomain System.AppDomain::CreateDomain")) {
- warn (string.Format (CultureInfo.CurrentCulture, Resources.XA2000, assembly));
- break;
- }
- }
- }
-
- bool MightNeedFix (TypeDefinition type)
- {
- return !type.IsAbstract && type.IsSubclassOf ("Java.Lang.Object", cache);
- }
-
- bool CompareTypes (TypeReference iType, TypeReference tType)
- {
- if (iType.IsGenericParameter)
- return true;
-
- if (iType.IsArray) {
- if (!tType.IsArray)
- return false;
- return CompareTypes (iType.GetElementType (), tType.GetElementType ());
- }
-
- if (iType.IsByReference) {
- if (!tType.IsByReference)
- return false;
- return CompareTypes (iType.GetElementType (), tType.GetElementType ());
- }
-
- if (iType.Name != tType.Name)
- return false;
-
- if (iType.Namespace != tType.Namespace)
- return false;
-
- TypeDefinition iTypeDef = cache.Resolve (iType);
- if (iTypeDef == null)
- return false;
-
- TypeDefinition tTypeDef = cache.Resolve (tType);
- if (tTypeDef == null)
- return false;
-
- if (iTypeDef.Module.FileName != tTypeDef.Module.FileName)
- return false;
-
- if (iType is Mono.Cecil.GenericInstanceType && tType is Mono.Cecil.GenericInstanceType) {
- GenericInstanceType iGType = iType as GenericInstanceType;
- GenericInstanceType tGType = tType as GenericInstanceType;
-
- if (iGType.GenericArguments.Count != tGType.GenericArguments.Count)
- return false;
- for (int i = 0; i < iGType.GenericArguments.Count; i++) {
- if (iGType.GenericArguments [i].IsGenericParameter)
- continue;
- if (!CompareTypes (iGType.GenericArguments [i], tGType.GenericArguments [i]))
- return false;
- }
- }
-
- return true;
- }
-
- bool IsInOverrides (MethodDefinition iMethod, MethodDefinition tMethod)
- {
- if (!tMethod.HasOverrides)
- return false;
-
- foreach (var o in tMethod.Overrides)
- if (o != null && iMethod.Name == o.Name && iMethod == cache.Resolve (o))
- return true;
-
- return false;
- }
-
- bool HaveSameSignature (TypeReference iface, MethodDefinition iMethod, MethodDefinition tMethod)
- {
- if (IsInOverrides (iMethod, tMethod))
- return true;
-
- if (iMethod.Name != tMethod.Name)
- return false;
-
- if (!CompareTypes (iMethod.MethodReturnType.ReturnType, tMethod.MethodReturnType.ReturnType))
- return false;
-
- if (iMethod.Parameters.Count != tMethod.Parameters.Count || iMethod.GenericParameters.Count != tMethod.GenericParameters.Count)
- return false;
-
- if (iMethod.HasParameters) {
- List m1p = new List (iMethod.Parameters);
- List m2p = new List (tMethod.Parameters);
-
- for (int i = 0; i < m1p.Count; i++) {
- if (!CompareTypes (m1p [i].ParameterType, m2p [i].ParameterType))
- return false;
- }
- }
-
- if (iMethod.HasGenericParameters) {
- List m1p = new List (iMethod.GenericParameters);
- List m2p = new List (tMethod.GenericParameters);
-
- for (int i = 0; i < m1p.Count; i++)
- if (!CompareTypes (m1p [i], m2p [i]))
- return false;
- }
-
- return true;
- }
-
- bool FixAbstractMethods (TypeDefinition type)
- {
- if (!type.HasInterfaces)
- return false;
-
- bool rv = false;
- List typeMethods = new List (type.Methods);
- foreach (var baseType in type.GetBaseTypes (cache))
- typeMethods.AddRange (baseType.Methods);
-
- foreach (var ifaceInfo in type.Interfaces) {
- var iface = ifaceInfo.InterfaceType;
- var ifaceDef = cache.Resolve (iface);
- if (ifaceDef == null) {
- LogMessage ($"Unable to unresolve interface: {iface.FullName}");
- continue;
- }
- if (ifaceDef.HasGenericParameters)
- continue;
-
- foreach (var iMethod in ifaceDef.Methods.Where (m => m.IsAbstract)) {
- bool exists = false;
-
- foreach (var tMethod in typeMethods) {
- if (HaveSameSignature (iface, iMethod, tMethod)) {
- exists = true;
- break;
- }
- }
-
- if (!exists) {
- AddNewExceptionMethod (type, iMethod);
- rv = true;
- }
- }
- }
-
- return rv;
- }
-
- TypeReference TryImportType (TypeDefinition declaringType, TypeReference type)
- {
- if (type.IsGenericParameter)
- return type;
-
- return declaringType.Module.ImportReference (type);
- }
-
- void AddNewExceptionMethod (TypeDefinition type, MethodDefinition method)
- {
- var newMethod = new MethodDefinition (method.Name, (method.Attributes | MethodAttributes.Final) & ~MethodAttributes.Abstract, TryImportType (type, method.ReturnType));
-
- foreach (var paramater in method.Parameters)
- newMethod.Parameters.Add (new ParameterDefinition (paramater.Name, paramater.Attributes, TryImportType (type, paramater.ParameterType)));
-
- var ilP = newMethod.Body.GetILProcessor ();
-
- ilP.Append (ilP.Create (Mono.Cecil.Cil.OpCodes.Newobj, type.Module.ImportReference (AbstractMethodErrorConstructor)));
- ilP.Append (ilP.Create (Mono.Cecil.Cil.OpCodes.Throw));
-
- type.Methods.Add (newMethod);
-
- LogMessage ($"Added method: {method} to type: {type.FullName} scope: {type.Scope}");
- }
-
- MethodDefinition abstractMethodErrorConstructor;
-
- MethodDefinition AbstractMethodErrorConstructor {
- get {
- if (abstractMethodErrorConstructor != null)
- return abstractMethodErrorConstructor;
-
- var assembly = GetMonoAndroidAssembly ();
- if (assembly != null) {
- var errorException = assembly.MainModule.GetType ("Java.Lang.AbstractMethodError");
- if (errorException != null) {
- foreach (var method in errorException.Methods) {
- if (method.Name == ".ctor" && !method.HasParameters) {
- abstractMethodErrorConstructor = method;
- break;
- }
- }
- }
- }
-
- if (abstractMethodErrorConstructor == null)
- throw new Exception ("Unable to find Java.Lang.AbstractMethodError constructor in Mono.Android assembly");
-
- return abstractMethodErrorConstructor;
- }
- }
-
- bool markedAbstractMethodErrorType;
-
- void MarkAbstractMethodErrorType ()
- {
- if (markedAbstractMethodErrorType)
- 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);
- }
-
- AssemblyDefinition GetMonoAndroidAssembly ()
- {
-#if !ILLINK
- return Context.Resolver.GetAssembly ("Mono.Android.dll");
-#else // ILLINK
- return Context.GetLoadedAssembly ("Mono.Android");
-#endif // ILLINK
+ context.IsAssemblyModified |= FixAbstractMethodsHelper.FixAbstractMethods (
+ assembly,
+ Context,
+ ref abstractMethodErrorCtor,
+ () => Context.GetAssembly ("Mono.Android"),
+ (msg) => LogMessage (msg));
}
}
}
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..add24c98cac
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/PostTrimmingFixAbstractMethodsStep.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using Java.Interop.Tools.Cecil;
+using Mono.Cecil;
+using Xamarin.Android.Tasks;
+
+namespace MonoDroid.Tuner;
+
+///
+/// Post-trimming version of FixAbstractMethods that calls FixAbstractMethodsHelper directly.
+/// Runs in after ILLink in the per-RID inner build.
+/// The helper has its own type-level guards (MightNeedFix checks IsSubclassOf Java.Lang.Object).
+///
+class PostTrimmingFixAbstractMethodsStep : IAssemblyModifierPipelineStep
+{
+ readonly IMetadataResolver cache;
+ readonly Func getMonoAndroidAssembly;
+ readonly Action logMessage;
+ readonly Action warn;
+ readonly HashSet warnedAssemblies = new (StringComparer.Ordinal);
+ MethodDefinition? abstractMethodErrorCtor;
+
+ public PostTrimmingFixAbstractMethodsStep (IMetadataResolver cache, Func getMonoAndroidAssembly, Action logMessage, Action warn)
+ {
+ this.cache = cache;
+ this.getMonoAndroidAssembly = getMonoAndroidAssembly;
+ this.logMessage = logMessage;
+ this.warn = warn;
+ }
+
+ public void ProcessAssembly (AssemblyDefinition assembly, StepContext context)
+ {
+ if (MonoAndroidHelper.IsFrameworkAssembly (assembly))
+ return;
+
+ FixAbstractMethodsHelper.CheckAppDomainUsage (assembly, warn, warnedAssemblies);
+
+ if (!assembly.MainModule.HasTypeReference ("Java.Lang.Object"))
+ return;
+
+ context.IsAssemblyModified |= FixAbstractMethodsHelper.FixAbstractMethods (assembly, cache, ref abstractMethodErrorCtor, getMonoAndroidAssembly, logMessage);
+ }
+}
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..671cca41e60 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssembliesNoShrink.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/LinkAssembliesNoShrink.cs
@@ -22,7 +22,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..8ba7b9d2544 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs
@@ -15,7 +15,7 @@ namespace Xamarin.Android.Tasks;
///
/// This opens each assembly once (via DirectoryAssemblyResolver with ReadWrite) and
/// runs all registered steps on it, then writes modified assemblies in-place. Currently
-/// runs StripEmbeddedLibrariesStep and (optionally) AddKeepAlivesStep.
+/// runs StripEmbeddedLibrariesStep, FixAbstractMethodsStep, and (optionally) AddKeepAlivesStep.
///
/// Runs in the inner build after ILLink but before ReadyToRun/crossgen2 compilation,
/// so that R2R images are generated from the already-modified assemblies.
@@ -47,6 +47,27 @@ public override bool RunTask ()
var steps = new List ();
steps.Add (new StripEmbeddedLibrariesStep (Log));
+
+ // Fix abstract methods: add stub implementations that throw Java.Lang.AbstractMethodError
+ // for unimplemented interface methods on Java.Lang.Object subclasses.
+ // Memoize the Mono.Android resolution so the attempt happens at most once.
+ AssemblyDefinition? monoAndroidAssembly = null;
+ bool monoAndroidResolutionAttempted = false;
+ steps.Add (new PostTrimmingFixAbstractMethodsStep (cache,
+ () => {
+ if (!monoAndroidResolutionAttempted) {
+ monoAndroidResolutionAttempted = true;
+ try {
+ monoAndroidAssembly = resolver.Resolve (AssemblyNameReference.Parse ("Mono.Android"));
+ } catch (AssemblyResolutionException ex) {
+ Log.LogErrorFromException (ex, showStackTrace: false);
+ }
+ }
+ return 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..3f5986c207c 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
@@ -7,8 +7,6 @@
using Java.Interop.Tools.Cecil;
using Mono.Cecil;
using Mono.Cecil.Cil;
-using Mono.Linker;
-using Mono.Linker.Steps;
using Mono.Tuner;
using MonoDroid.Tuner;
using NUnit.Framework;
@@ -27,30 +25,34 @@ void Logger (TraceLevel level, string message) =>
public void FixAbstractMethodsStep_SkipDimMembers ()
{
var path = Path.Combine (Root, "temp", TestName);
- var step = new FixAbstractMethodsStep ();
Directory.CreateDirectory (path);
- using (var resolver = new DirectoryAssemblyResolver (Logger, false))
- using (var context = new LinkContext (resolver)) {
- step.Initialize (context, new EmptyMarkContext ());
+ using (var resolver = new DirectoryAssemblyResolver (Logger, false)) {
+ var cache = new TypeDefinitionCache ();
resolver.SearchDirectories.Add (path);
var myAssemblyPath = Path.Combine (path, "MyAssembly.dll");
+ var monoAndroidPath = Path.Combine (path, "Mono.Android.dll");
using (var android = CreateFauxMonoAndroidAssembly ()) {
- android.Write (Path.Combine (path, "Mono.Android.dll"));
+ android.Write (monoAndroidPath);
CreateAbstractIfaceImplementation (myAssemblyPath, android);
}
- using (var assm = context.Resolve ("MyAssembly")) {
- step.FixAbstractMethods (assm);
+ var assm = resolver.GetAssembly (myAssemblyPath);
+ MethodDefinition? errorCtor = null;
+ FixAbstractMethodsHelper.FixAbstractMethods (
+ assm,
+ cache,
+ ref errorCtor,
+ () => resolver.GetAssembly (monoAndroidPath),
+ (msg) => TestContext.WriteLine (msg));
- var impl = assm.MainModule.GetType ("MyNamespace.MyClass");
+ var impl = assm.MainModule.GetType ("MyNamespace.MyClass");
- Assert.IsTrue (impl.Methods.Any (m => m.Name == "MyAbstractMethod"), "We should have generated an override for MyAbstractMethod");
- Assert.IsFalse (impl.Methods.Any (m => m.Name == "MyDefaultMethod"), "We should not have generated an override for MyDefaultMethod");
- }
+ Assert.IsTrue (impl.Methods.Any (m => m.Name == "MyAbstractMethod"), "We should have generated an override for MyAbstractMethod");
+ Assert.IsFalse (impl.Methods.Any (m => m.Name == "MyDefaultMethod"), "We should not have generated an override for MyDefaultMethod");
}
Directory.Delete (path, true);
@@ -86,32 +88,36 @@ static void CreateAbstractIfaceImplementation (string assemblyPath, AssemblyDefi
public void FixAbstractMethodsStep_Explicit ()
{
var path = Path.Combine (Root, "temp", TestName);
- var step = new FixAbstractMethodsStep ();
Directory.CreateDirectory (path);
- using (var resolver = new DirectoryAssemblyResolver (Logger, false))
- using (var context = new LinkContext (resolver)) {
- step.Initialize (context, new EmptyMarkContext ());
+ using (var resolver = new DirectoryAssemblyResolver (Logger, false)) {
+ var cache = new TypeDefinitionCache ();
resolver.SearchDirectories.Add (path);
var myAssemblyPath = Path.Combine (path, "MyAssembly.dll");
+ var monoAndroidPath = Path.Combine (path, "Mono.Android.dll");
using (var android = CreateFauxMonoAndroidAssembly ()) {
- android.Write (Path.Combine (path, "Mono.Android.dll"));
+ android.Write (monoAndroidPath);
CreateExplicitInterface (myAssemblyPath, android);
}
- using (var assm = context.Resolve ("MyAssembly")) {
- step.FixAbstractMethods (assm);
-
- var impl = assm.MainModule.GetType ("MyNamespace.MyClass");
- Assert.AreEqual (2, impl.Methods.Count, "MyClass should contain 2 methods");
- var method = impl.Methods.FirstOrDefault (m => m.Name == "MyNamespace.IMyInterface.MyMethod");
- Assert.IsNotNull (method, "MyNamespace.IMyInterface.MyMethod should exist");
- method = impl.Methods.FirstOrDefault (m => m.Name == "MyMissingMethod");
- Assert.IsNotNull (method, "MyMissingMethod should exist");
- }
+ var assm = resolver.GetAssembly (myAssemblyPath);
+ MethodDefinition? errorCtor = null;
+ FixAbstractMethodsHelper.FixAbstractMethods (
+ assm,
+ cache,
+ ref errorCtor,
+ () => resolver.GetAssembly (monoAndroidPath),
+ (msg) => TestContext.WriteLine (msg));
+
+ var impl = assm.MainModule.GetType ("MyNamespace.MyClass");
+ Assert.AreEqual (2, impl.Methods.Count, "MyClass should contain 2 methods");
+ var method = impl.Methods.FirstOrDefault (m => m.Name == "MyNamespace.IMyInterface.MyMethod");
+ Assert.IsNotNull (method, "MyNamespace.IMyInterface.MyMethod should exist");
+ method = impl.Methods.FirstOrDefault (m => m.Name == "MyMissingMethod");
+ Assert.IsNotNull (method, "MyMissingMethod should exist");
}
Directory.Delete (path, true);
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..bdc11cd5507 100644
--- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj
+++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj
@@ -45,7 +45,7 @@
-
+
@@ -56,6 +56,8 @@
+
+