[linker] Preserve smart enum conversion methods when needed.

This commit is contained in:
Rolf Bjarne Kvinge 2017-07-06 10:42:27 +02:00
Родитель 9041503149
Коммит 34f31b5b34
8 изменённых файлов: 214 добавлений и 0 удалений

Просмотреть файл

@ -1201,6 +1201,10 @@ Something unexpected occured when trying to mark `NSObject` subclasses from the
Something unexpected occured when trying to inline code from the application. The assembly causing the issue is named in the error message. In order to fix this issue the assembly will need to be provided in a [bug report](https://bugzilla.xamarin.com) along with a complete build log with verbosity enabled (i.e. `-v -v -v -v` in the **Additional mtouch arguments**). Something unexpected occured when trying to inline code from the application. The assembly causing the issue is named in the error message. In order to fix this issue the assembly will need to be provided in a [bug report](https://bugzilla.xamarin.com) along with a complete build log with verbosity enabled (i.e. `-v -v -v -v` in the **Additional mtouch arguments**).
### <a name="MT2100"/>MT2100: Smart Enum Conversion Preserver failed processing `...`.
Something unexpected occured when trying to mark the conversion methods for smart enums from the application. The assembly causing the issue is named in the error message. In order to fix this issue the assembly will need to be provided in a [bug report](https://bugzilla.xamarin.com) along with a complete build log with verbosity enabled (i.e. `-v -v -v -v` in the **Additional mtouch arguments**).
<!-- MT21xx: more linker errors --> <!-- MT21xx: more linker errors -->
<!--- 2100 used by mmp --> <!--- 2100 used by mmp -->

Просмотреть файл

@ -164,5 +164,60 @@ namespace LinkAll.Attributes {
var method = klass.GetMethod ("GetHandle", BindingFlags.Public | BindingFlags.Static); var method = klass.GetMethod ("GetHandle", BindingFlags.Public | BindingFlags.Static);
Assert.NotNull (method, "GetHandle"); Assert.NotNull (method, "GetHandle");
} }
[Test]
public void SmartEnumTest ()
{
var consumer = GetType ().Assembly.GetType ("LinkAll.Attributes.SmartConsumer");
Assert.NotNull (consumer, "SmartConsumer");
Assert.NotNull (consumer.GetMethod ("GetSmartEnumValue"), "GetSmartEnumValue");
Assert.NotNull (consumer.GetMethod ("SetSmartEnumValue"), "SetSmartEnumValue");
var smartEnum = GetType ().Assembly.GetType ("LinkAll.Attributes.SmartEnum");
Assert.NotNull (smartEnum, "SmartEnum");
var smartExtensions = GetType ().Assembly.GetType ("LinkAll.Attributes.SmartEnumExtensions");
Assert.NotNull (smartExtensions, "SmartEnumExtensions");
Assert.NotNull (smartExtensions.GetMethod ("GetConstant"), "GetConstant");
Assert.NotNull (smartExtensions.GetMethod ("GetValue"), "GetValue");
// Unused smart enums and their extensions should be linked away
Assert.IsNull (typeof (NSObject).Assembly.GetType ("AVFoundation.AVMediaTypes"), "AVMediaTypes");
Assert.IsNull (typeof (NSObject).Assembly.GetType ("AVFoundation.AVMediaTypesExtensions"), "AVMediaTypesExtensions");
} }
}
[Preserve (AllMembers = true)]
class SmartConsumer : NSObject
{
// The Smart Get/Set methods should not be linked away, and neither should the Smart enums + extensions
[Export ("getSmartEnumValue")]
[return: BindAs (typeof (SmartEnum), OriginalType = typeof (NSString))]
public SmartEnum GetSmartEnumValue ()
{
return SmartEnum.Smart;
}
[Export ("setSmartEnumValue:")]
public void SetSmartEnumValue ([BindAs (typeof (SmartEnum), OriginalType = typeof (NSString))] SmartEnum value)
{
}
}
public enum SmartEnum : int
{
Smart = 0,
}
public static class SmartEnumExtensions
{
public static NSString GetConstant (this SmartEnum self)
{
return (NSString) "Smart";
}
public static SmartEnum GetValue (NSString constant)
{
return SmartEnum.Smart;
}
}
} }

Просмотреть файл

@ -0,0 +1,146 @@
// Copyright 2017 Xamarin Inc.
using System;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Linker;
using Mono.Tuner;
using MonoTouch.Tuner;
namespace Xamarin.Linker.Steps
{
public class PreserveSmartEnumConversionsSubStep : ExceptionalSubStep
{
Dictionary<TypeDefinition, Tuple<MethodDefinition, MethodDefinition>> cache;
protected override string Name { get; } = "Smart Enum Conversion Preserver";
protected override int ErrorCode { get; } = 2100;
public override SubStepTargets Targets {
get {
return SubStepTargets.Method | SubStepTargets.Property;
}
}
public override bool IsActiveFor (AssemblyDefinition assembly)
{
// we need to process all assemblies, because the functions we want to
// preserve are not necessarily in the assembly we're processing.
return true;
}
void Preserve (Tuple<MethodDefinition, MethodDefinition> pair, MethodDefinition conditionA, MethodDefinition conditionB = null)
{
if (conditionA != null) {
context.Annotations.AddPreservedMethod (conditionA, pair.Item1);
context.Annotations.AddPreservedMethod (conditionA, pair.Item2);
}
if (conditionB != null) {
context.Annotations.AddPreservedMethod (conditionB, pair.Item1);
context.Annotations.AddPreservedMethod (conditionB, pair.Item2);
}
}
void ProcessAttributeProvider (ICustomAttributeProvider provider, MethodDefinition conditionA, MethodDefinition conditionB = null)
{
if (provider?.HasCustomAttributes != true)
return;
foreach (var ca in provider.CustomAttributes) {
var tr = ca.Constructor.DeclaringType;
if (!tr.IsPlatformType ("ObjCRuntime", "BindAsAttribute"))
continue;
if (ca.ConstructorArguments.Count != 1) {
Console.WriteLine ("WARNING");
continue;
}
var managedType = ca.ConstructorArguments [0].Value as TypeReference;
var managedEnumType = managedType?.GetElementType ().Resolve ();
if (managedEnumType == null) {
Console.WriteLine ("WARNING");
continue;
}
Tuple<MethodDefinition, MethodDefinition> pair;
if (cache != null && cache.TryGetValue (managedEnumType, out pair)) {
Preserve (pair, conditionA, conditionB);
continue;
}
Console.WriteLine (managedEnumType);
// Find the Extension type
TypeDefinition extensionType = null;
var extensionName = managedEnumType.Name + "Extensions";
foreach (var type in managedEnumType.Module.Types) {
if (type.Namespace != managedEnumType.Namespace)
continue;
if (type.Name != extensionName)
continue;
extensionType = type;
break;
}
if (extensionType == null) {
Console.WriteLine ("WARNING");
continue;
}
// Find the GetConstant/GetValue methods
MethodDefinition getConstant = null;
MethodDefinition getValue = null;
foreach (var method in extensionType.Methods) {
if (!method.IsStatic)
continue;
if (!method.HasParameters || method.Parameters.Count != 1)
continue;
if (method.Name == "GetConstant") {
if (!method.ReturnType.IsPlatformType ("Foundation", "NSString"))
continue;
if (method.Parameters [0].ParameterType != managedEnumType)
continue;
getConstant = method;
} else if (method.Name == "GetValue") {
if (!method.Parameters [0].ParameterType.IsPlatformType ("Foundation", "NSString"))
continue;
if (method.ReturnType != managedEnumType)
continue;
getValue = method;
}
}
if (getConstant == null || getValue == null) {
Console.WriteLine ("WARNING");
continue;
}
pair = new Tuple<MethodDefinition, MethodDefinition> (getConstant, getValue);
if (cache == null)
cache = new Dictionary<TypeDefinition, Tuple<MethodDefinition, MethodDefinition>> ();
cache.Add (managedEnumType, pair);
Preserve (pair, conditionA, conditionB);
}
}
protected override void Process (MethodDefinition method)
{
ProcessAttributeProvider (method, method);
ProcessAttributeProvider (method.MethodReturnType, method);
if (method.HasParameters) {
foreach (var p in method.Parameters)
ProcessAttributeProvider (p, method);
}
}
protected override void Process (PropertyDefinition property)
{
ProcessAttributeProvider (property, property.GetMethod, property.SetMethod);
if (property.GetMethod != null)
Process (property.GetMethod);
if (property.SetMethod != null)
Process (property.SetMethod);
}
}
}

Просмотреть файл

@ -94,6 +94,7 @@ tuner_sources = \
../linker/MonoTouch.Tuner/Extensions.cs \ ../linker/MonoTouch.Tuner/Extensions.cs \
../linker/MonoTouch.Tuner/ListExportedSymbols.cs \ ../linker/MonoTouch.Tuner/ListExportedSymbols.cs \
../linker/MonoTouch.Tuner/ProcessExportedFields.cs \ ../linker/MonoTouch.Tuner/ProcessExportedFields.cs \
../linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs \
../../src/build/mac/Constants.cs ../../src/build/mac/Constants.cs
linker_resources = \ linker_resources = \

Просмотреть файл

@ -265,6 +265,9 @@
<Compile Include="..\linker\MonoTouch.Tuner\ProcessExportedFields.cs"> <Compile Include="..\linker\MonoTouch.Tuner\ProcessExportedFields.cs">
<Link>MonoTouch.Tuner\ProcessExportedFields.cs</Link> <Link>MonoTouch.Tuner\ProcessExportedFields.cs</Link>
</Compile> </Compile>
<Compile Include="..\linker\MonoTouch.Tuner\PreserveSmartEnumConversionsSubStep.cs">
<Link>MonoTouch.Tuner\PreserveSmartEnumConversionsSubStep.cs</Link>
</Compile>
<Compile Include="..\..\src\build\mac\Constants.cs"> <Compile Include="..\..\src\build\mac\Constants.cs">
<Link>external\Constants.cs</Link> <Link>external\Constants.cs</Link>
</Compile> </Compile>

Просмотреть файл

@ -71,6 +71,7 @@ LINKER_SOURCES = \
$(LINKER_DIR)/MonoTouch.Tuner/RemoveAttributes.cs \ $(LINKER_DIR)/MonoTouch.Tuner/RemoveAttributes.cs \
$(LINKER_DIR)/MonoTouch.Tuner/RemoveCode.cs \ $(LINKER_DIR)/MonoTouch.Tuner/RemoveCode.cs \
$(LINKER_DIR)/MonoTouch.Tuner/SealerSubStep.cs \ $(LINKER_DIR)/MonoTouch.Tuner/SealerSubStep.cs \
$(LINKER_DIR)/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs \
$(TOP)/tools/linker/ApplyPreserveAttribute.cs \ $(TOP)/tools/linker/ApplyPreserveAttribute.cs \
$(TOP)/tools/linker/BaseProfile.cs \ $(TOP)/tools/linker/BaseProfile.cs \
$(TOP)/tools/linker/CoreHttpMessageHandler.cs \ $(TOP)/tools/linker/CoreHttpMessageHandler.cs \

Просмотреть файл

@ -157,6 +157,7 @@ namespace MonoTouch.Tuner {
sub.Add (new PreserveSoapHttpClients ()); sub.Add (new PreserveSoapHttpClients ());
sub.Add (new CoreHttpMessageHandler (options)); sub.Add (new CoreHttpMessageHandler (options));
sub.Add (new InlinerSubStep ()); sub.Add (new InlinerSubStep ());
sub.Add (new PreserveSmartEnumConversionsSubStep ());
return sub; return sub;
} }

Просмотреть файл

@ -335,6 +335,9 @@
<Compile Include="..\linker\MonoTouch.Tuner\InlinerSubStep.cs"> <Compile Include="..\linker\MonoTouch.Tuner\InlinerSubStep.cs">
<Link>MonoTouch.Tuner\InlinerSubStep.cs</Link> <Link>MonoTouch.Tuner\InlinerSubStep.cs</Link>
</Compile> </Compile>
<Compile Include="..\linker\MonoTouch.Tuner\PreserveSmartEnumConversionsSubStep.cs">
<Link>MonoTouch.Tuner\PreserveSmartEnumConversionsSubStep.cs</Link>
</Compile>
<Compile Include="..\common\BuildTasks.cs"> <Compile Include="..\common\BuildTasks.cs">
<Link>common\BuildTasks.cs</Link> <Link>common\BuildTasks.cs</Link>
</Compile> </Compile>