Skip to content

Commit

Permalink
Use LinkContext's type resolve cache
Browse files Browse the repository at this point in the history
This allows the custom steps to be added using the supported mechanism,
one at a time, instead of injecting them using reflection.

Depends on dotnet#5870.
  • Loading branch information
sbomer committed Apr 27, 2021
1 parent 0ec5e18 commit dac30f3
Show file tree
Hide file tree
Showing 11 changed files with 268 additions and 112 deletions.
161 changes: 161 additions & 0 deletions src/Microsoft.Android.Sdk.ILLink/LinkContextExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#nullable enable

using Mono.Cecil;
using Mono.Collections.Generic;
using Mono.Linker;
using System.Collections.Generic;

namespace Microsoft.Android.Sdk.ILLink
{
public static class LinkContextExtensions
{
public static MethodDefinition GetBaseDefinition (this LinkContext context, MethodDefinition method)
{
if (method.IsStatic || method.IsNewSlot || !method.IsVirtual)
return method;

foreach (var baseType in context.GetBaseTypes (method.DeclaringType)) {
foreach (var m in baseType.Methods) {
if (!m.IsConstructor &&
m.Name == method.Name &&
(m.IsVirtual || m.IsAbstract) &&
context.AreParametersCompatibleWith (m.Parameters, method.Parameters)) {
return m;
}
}
}
return method;
}

public static bool AreParametersCompatibleWith (this LinkContext context, Collection<ParameterDefinition> a, Collection<ParameterDefinition> b)
{
if (a.Count != b.Count)
return false;

if (a.Count == 0)
return true;

for (int i = 0; i < a.Count; i++)
if (!context.IsParameterCompatibleWith (a [i].ParameterType, b [i].ParameterType))
return false;

return true;
}

static bool IsParameterCompatibleWith (this LinkContext context, IModifierType a, IModifierType b)
{
if (!context.IsParameterCompatibleWith (a.ModifierType, b.ModifierType))
return false;

return context.IsParameterCompatibleWith (a.ElementType, b.ElementType);
}

static bool IsParameterCompatibleWith (this LinkContext context, TypeSpecification a, TypeSpecification b)
{
if (a is GenericInstanceType)
return context.IsParameterCompatibleWith ((GenericInstanceType) a, (GenericInstanceType) b);

if (a is IModifierType)
return context.IsParameterCompatibleWith ((IModifierType) a, (IModifierType) b);

return context.IsParameterCompatibleWith (a.ElementType, b.ElementType);
}

static bool IsParameterCompatibleWith (this LinkContext context, GenericInstanceType a, GenericInstanceType b)
{
if (!context.IsParameterCompatibleWith (a.ElementType, b.ElementType))
return false;

if (a.GenericArguments.Count != b.GenericArguments.Count)
return false;

if (a.GenericArguments.Count == 0)
return true;

for (int i = 0; i < a.GenericArguments.Count; i++)
if (!context.IsParameterCompatibleWith (a.GenericArguments [i], b.GenericArguments [i]))
return false;

return true;
}

static bool IsParameterCompatibleWith (this LinkContext context, TypeReference a, TypeReference b)
{
if (a is TypeSpecification || b is TypeSpecification) {
if (a.GetType () != b.GetType ())
return false;

return context.IsParameterCompatibleWith ((TypeSpecification) a, (TypeSpecification) b);
}

if (a.IsGenericParameter) {
if (b.IsGenericParameter && a.Name == b.Name)
return true;
var gpa = (GenericParameter) a;
foreach (var c in gpa.Constraints) {
if (!context.IsAssignableFrom (c.ConstraintType, b))
return false;
}
return true;
}

return a.FullName == b.FullName;
}

public static TypeDefinition? GetBaseType (this LinkContext context, TypeDefinition type)
{
var bt = type.BaseType;
if (bt == null)
return null;
return context.ResolveTypeDefinition (bt);
}

public static IEnumerable<TypeDefinition> GetTypeAndBaseTypes (this LinkContext context, TypeDefinition type)
{
TypeDefinition? t = type;

while (t != null) {
yield return t;
t = context.GetBaseType (t);
}
}

public static IEnumerable<TypeDefinition> GetBaseTypes (this LinkContext context, TypeDefinition type)
{
TypeDefinition? t = type;

while ((t = context.GetBaseType (t)) != null) {
yield return t;
}
}

public static bool IsAssignableFrom (this LinkContext context, TypeReference type, TypeReference c)
{
if (type.FullName == c.FullName)
return true;
var d = context.ResolveTypeDefinition (c);
if (d == null)
return false;
foreach (var t in context.GetTypeAndBaseTypes (d)) {
if (type.FullName == t.FullName)
return true;
foreach (var ifaceImpl in t.Interfaces) {
var i = ifaceImpl.InterfaceType;
if (context.IsAssignableFrom (type, i))
return true;
}
}
return false;
}

public static bool IsSubclassOf (this LinkContext context, TypeDefinition type, string typeName)
{
foreach (var t in context.GetTypeAndBaseTypes (type)) {
if (t.FullName == typeName) {
return true;
}
}
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\Configuration.props" />
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<DefineConstants>NET5_LINKER</DefineConstants>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<OutputPath>$(XAInstallPrefix)xbuild\Xamarin\Android\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.ILLink" Version="$(MicrosoftNETILLinkPackageVersion)" />
<PackageReference Include="Microsoft.NET.ILLink" Version="$(MicrosoftNETILLinkTasksPackageVersion)" />
<ProjectReference Include="..\..\external\Java.Interop\src\Java.Interop.Export\Java.Interop.Export.csproj" SkipGetTargetFrameworkProperties="true" AdditionalProperties="TargetFramework=netcoreapp3.1" />

<Compile Include="..\Xamarin.Android.Build.Tasks\obj\$(Configuration)\Profile.g.cs" Link="Profile.g.cs" />
Expand Down Expand Up @@ -41,9 +41,6 @@

<!--Other Xamarin.Android / Java.Interop files-->
<Compile Include="..\..\external\Java.Interop\src\Java.Interop.Tools.Cecil\Java.Interop.Tools.Cecil\CustomAttributeProviderRocks.cs" Link="Java.Interop\CustomAttributeProviderRocks.cs" />
<Compile Include="..\..\external\Java.Interop\src\Java.Interop.Tools.Cecil\Java.Interop.Tools.Cecil\MethodDefinitionRocks.cs" Link="Java.Interop\MethodDefinitionRocks.cs" />
<Compile Include="..\..\external\Java.Interop\src\Java.Interop.Tools.Cecil\Java.Interop.Tools.Cecil\TypeDefinitionCache.cs" Link="Java.Interop\TypeDefinitionCache.cs" />
<Compile Include="..\..\external\Java.Interop\src\Java.Interop.Tools.Cecil\Java.Interop.Tools.Cecil\TypeDefinitionRocks.cs" Link="Java.Interop\TypeDefinitionRocks.cs" />
<Compile Include="..\Xamarin.Android.Build.Tasks\Utilities\MonoAndroidHelper.Linker.cs" Link="Utilities\MonoAndroidHelper.Linker.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
6 changes: 1 addition & 5 deletions src/Microsoft.Android.Sdk.ILLink/PreserveRegistrations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,8 @@ namespace Microsoft.Android.Sdk.ILLink
{
class PreserveRegistrations : IMarkHandler
{
readonly TypeDefinitionCache cache;

LinkContext context;

public PreserveRegistrations (TypeDefinitionCache cache) => this.cache = cache;

public void Initialize (LinkContext context, MarkContext markContext)
{
this.context = context;
Expand Down Expand Up @@ -81,7 +77,7 @@ void ProcessMethod (MethodDefinition method)
if (!method.TryGetRegisterMember (out var member, out var nativeMethod, out var signature)) {
if (PreserveJniMarshalMethods () &&
method.DeclaringType.GetMarshalMethodsType () != null &&
method.TryGetBaseOrInterfaceRegisterMember (cache, out member, out nativeMethod, out signature)) {
context.TryGetBaseOrInterfaceRegisterMember (method, out member, out nativeMethod, out signature)) {
preserveJniMarshalMethodOnly = true;
} else {
return;
Expand Down
19 changes: 19 additions & 0 deletions src/Microsoft.Android.Sdk.ILLink/PreserveSubStepDispatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
using Mono.Linker.Steps;
using Mono.Tuner;

namespace Microsoft.Android.Sdk.ILLink
{
public class PreserveSubStepDispatcher : MarkSubStepsDispatcher
{
public PreserveSubStepDispatcher ()
: base (new List<ISubStep> () {
new ApplyPreserveAttribute (),
new PreserveExportedTypes ()
})
{
}
}
}
69 changes: 1 addition & 68 deletions src/Microsoft.Android.Sdk.ILLink/SetupStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,77 +12,10 @@ namespace Microsoft.Android.Sdk.ILLink
{
class SetupStep : BaseStep
{
List<IStep> _steps;
List<IMarkHandler> _markHandlers;

List<IStep> Steps {
get {
if (_steps == null) {
var pipeline = typeof (LinkContext).GetProperty ("Pipeline").GetGetMethod ().Invoke (Context, null);
_steps = (List<IStep>) pipeline.GetType ().GetField ("_steps", BindingFlags.Instance | BindingFlags.NonPublic).GetValue (pipeline);
//foreach (var step in _steps)
// Console.WriteLine ($"step: {step.GetType ().Name}");
}
return _steps;
}
}

List<IMarkHandler> MarkHandlers {
get {
if (_markHandlers == null) {
var pipeline = typeof (LinkContext).GetProperty ("Pipeline").GetGetMethod ().Invoke (Context, null);
_markHandlers = (List<IMarkHandler>) pipeline.GetType ().GetProperty ("MarkHandlers").GetValue (pipeline);
}
return _markHandlers;
}
}

protected override void Process ()
{
string tfmPaths;
if (Context.TryGetCustomData ("XATargetFrameworkDirectories", out tfmPaths))
if (Context.TryGetCustomData ("XATargetFrameworkDirectories", out string tfmPaths))
Xamarin.Android.Tasks.MonoAndroidHelper.TargetFrameworkDirectories = tfmPaths.Split (new char [] { ';' });

MarkHandlers.Add (new SubStepDispatcher (new List<ISubStep> () {
new ApplyPreserveAttribute (),
new PreserveExportedTypes ()
}));

var cache = new TypeDefinitionCache ();
MarkHandlers.Add (new MarkJavaObjects ());
MarkHandlers.Add (new PreserveJavaExceptions ());
MarkHandlers.Add (new PreserveApplications ());
MarkHandlers.Add (new PreserveRegistrations (cache));
MarkHandlers.Add (new PreserveJavaInterfaces ());

MarkHandlers.Add (new FixAbstractMethodsStep (cache));

string proguardPath;
if (Context.TryGetCustomData ("ProguardConfiguration", out proguardPath))
InsertAfter (new GenerateProguardConfiguration (proguardPath), "CleanStep");

string addKeepAlivesStep;
if (Context.TryGetCustomData ("AddKeepAlivesStep", out addKeepAlivesStep) && bool.TryParse (addKeepAlivesStep, out var bv) && bv)
InsertAfter (new AddKeepAlivesStep (cache), "CleanStep");

string androidLinkResources;
if (Context.TryGetCustomData ("AndroidLinkResources", out androidLinkResources) && bool.TryParse (androidLinkResources, out var linkResources) && linkResources) {
InsertAfter (new RemoveResourceDesignerStep (), "CleanStep");
InsertAfter (new GetAssembliesStep (), "CleanStep");
}
InsertAfter (new StripEmbeddedLibraries (), "CleanStep");
}

void InsertAfter (IStep step, string stepName)
{
for (int i = 0; i < Steps.Count;) {
if (Steps [i++].GetType ().Name == stepName) {
Steps.Insert (i, step);
return;
}
}

throw new InvalidOperationException ($"Could not insert {step} after {stepName}.");
}
}
}
14 changes: 0 additions & 14 deletions src/Microsoft.Android.Sdk.ILLink/SubStepDispatcher.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,22 @@
using Mono.Linker.Steps;

using Mono.Cecil.Cil;
#if NET5_LINKER
using Microsoft.Android.Sdk.ILLink;
#endif

namespace MonoDroid.Tuner
{
public class AddKeepAlivesStep : BaseStep
{
#if !NET5_LINKER
readonly TypeDefinitionCache cache;

public AddKeepAlivesStep (TypeDefinitionCache cache)
{
this.cache = cache;
}
#endif

protected override void ProcessAssembly (AssemblyDefinition assembly)
{
Expand Down Expand Up @@ -65,7 +70,11 @@ static void AddNestedTypes (List<TypeDefinition> types, TypeDefinition type)

bool MightNeedFix (TypeDefinition type)
{
#if NET5_LINKER
return !type.IsAbstract && Context.IsSubclassOf (type, "Java.Lang.Object");
#else
return !type.IsAbstract && type.IsSubclassOf ("Java.Lang.Object", cache);
#endif
}

MethodDefinition methodKeepAlive = null;
Expand Down
Loading

0 comments on commit dac30f3

Please sign in to comment.