2016-04-21 15:57:02 +03:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
|
|
using Mono.Linker;
|
|
|
|
using Mono.Linker.Steps;
|
|
|
|
|
|
|
|
using Mono.Cecil;
|
|
|
|
using Mono.Cecil.Cil;
|
|
|
|
|
|
|
|
using Mono.Tuner;
|
|
|
|
|
2017-02-10 09:45:42 +03:00
|
|
|
using Xamarin.Tuner;
|
|
|
|
|
2016-04-21 15:57:02 +03:00
|
|
|
namespace Xamarin.Linker.Steps {
|
|
|
|
|
|
|
|
public class RemoveSelectors : IStep {
|
|
|
|
|
|
|
|
public void Process (LinkContext context)
|
|
|
|
{
|
|
|
|
var profile = (Profile.Current as BaseProfile);
|
|
|
|
|
|
|
|
AssemblyDefinition assembly;
|
|
|
|
if (!context.TryGetLinkedAssembly (profile.ProductAssembly, out assembly))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// skip this if we're not linking monotouch, e.g. --linkskip=monotouch
|
|
|
|
if (context.Annotations.GetAction (assembly) != AssemblyAction.Link)
|
|
|
|
return;
|
|
|
|
|
|
|
|
foreach (TypeDefinition type in assembly.MainModule.Types) {
|
2017-02-10 09:45:42 +03:00
|
|
|
ProcessType (type, (DerivedLinkContext) context);
|
2016-04-21 15:57:02 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-10 09:45:42 +03:00
|
|
|
void ProcessType (TypeDefinition type, DerivedLinkContext context)
|
2016-04-21 15:57:02 +03:00
|
|
|
{
|
2017-02-10 09:45:42 +03:00
|
|
|
if (type.IsNSObject (context)) {
|
2016-04-21 15:57:02 +03:00
|
|
|
ProcessNSObject (type);
|
|
|
|
} else if (type.HasNestedTypes) {
|
|
|
|
foreach (var nested in type.NestedTypes)
|
2017-02-10 09:45:42 +03:00
|
|
|
ProcessType (nested, context);
|
2016-04-21 15:57:02 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessNSObject (TypeDefinition type)
|
|
|
|
{
|
|
|
|
var selectors = PopulateSelectors (type);
|
|
|
|
if (selectors == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
foreach (var method in CollectMethods (type))
|
|
|
|
CheckSelectorUsage (method, selectors);
|
|
|
|
// nested types can also use the selectors, see bug #1516 for Appearance support
|
|
|
|
if (type.HasNestedTypes) {
|
|
|
|
foreach (TypeDefinition nested in type.NestedTypes) {
|
|
|
|
foreach (var nm in CollectMethods (nested))
|
|
|
|
CheckSelectorUsage (nm, selectors);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selectors.Count == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
PatchStaticConstructor (type, selectors);
|
|
|
|
RemoveUnusedSelectors (type, selectors);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void CheckSelectorUsage (MethodDefinition method, HashSet<FieldDefinition> selectors)
|
|
|
|
{
|
|
|
|
if (!method.HasBody)
|
|
|
|
return;
|
|
|
|
|
|
|
|
foreach (Instruction instruction in method.Body.Instructions) {
|
|
|
|
switch (instruction.OpCode.OperandType) {
|
|
|
|
case OperandType.InlineTok:
|
|
|
|
case OperandType.InlineField:
|
2017-05-18 15:45:23 +03:00
|
|
|
var field = (instruction.Operand as FieldReference)?.Resolve ();
|
2016-04-21 15:57:02 +03:00
|
|
|
if (field == null)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (selectors.Contains (field))
|
|
|
|
selectors.Remove (field);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PatchStaticConstructor (TypeDefinition type, HashSet<FieldDefinition> selectors)
|
|
|
|
{
|
|
|
|
var cctor = type.GetTypeConstructor ();
|
|
|
|
if (cctor == null || !cctor.HasBody)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var instructions = cctor.Body.Instructions;
|
|
|
|
|
|
|
|
for (int i = 0; i < instructions.Count; i++) {
|
|
|
|
var instruction = instructions [i];
|
|
|
|
if (!IsCreateSelector (instruction, selectors))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
instructions.RemoveAt (i--);
|
|
|
|
instructions.RemoveAt (i--);
|
|
|
|
instructions.RemoveAt (i--);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsCreateSelector (Instruction instruction, HashSet<FieldDefinition> selectors)
|
|
|
|
{
|
|
|
|
if (instruction.OpCode != OpCodes.Stsfld)
|
|
|
|
return false;
|
|
|
|
|
2017-05-18 15:45:23 +03:00
|
|
|
var field = (instruction.Operand as FieldReference)?.Resolve ();
|
2016-04-21 15:57:02 +03:00
|
|
|
if (field == null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!selectors.Contains (field))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
instruction = instruction.Previous;
|
|
|
|
if (instruction == null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (instruction.OpCode != OpCodes.Call)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!IsRegisterSelector (instruction.Operand as MethodReference))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
instruction = instruction.Previous;
|
|
|
|
if (instruction == null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (instruction.OpCode != OpCodes.Ldstr)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsRegisterSelector (MethodReference method)
|
|
|
|
{
|
|
|
|
if (method == null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (method.Name != "GetHandle" && method.Name != "sel_registerName")
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!method.DeclaringType.Is (Namespaces.ObjCRuntime, "Selector"))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void RemoveUnusedSelectors (TypeDefinition type, HashSet<FieldDefinition> selectors)
|
|
|
|
{
|
|
|
|
var fields = type.Fields;
|
|
|
|
|
|
|
|
for (int i = 0; i < fields.Count; i++)
|
|
|
|
if (selectors.Contains (fields [i]))
|
|
|
|
fields.RemoveAt (i--);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HashSet<FieldDefinition> PopulateSelectors (TypeDefinition type)
|
|
|
|
{
|
|
|
|
if (!type.HasFields)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
HashSet<FieldDefinition> selectors = null;
|
|
|
|
|
|
|
|
foreach (FieldDefinition field in type.Fields) {
|
|
|
|
if (!IsSelector (field))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (selectors == null)
|
|
|
|
selectors = new HashSet<FieldDefinition> ();
|
|
|
|
|
|
|
|
selectors.Add (field);
|
|
|
|
}
|
|
|
|
|
|
|
|
return selectors;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool IsSelector (FieldDefinition field)
|
|
|
|
{
|
|
|
|
if (!field.IsStatic)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!field.FieldType.Is ("System", "IntPtr"))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!field.Name.StartsWith ("sel", StringComparison.Ordinal))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static IEnumerable<MethodDefinition> CollectMethods (TypeDefinition type)
|
|
|
|
{
|
|
|
|
if (type.HasMethods) {
|
|
|
|
foreach (MethodDefinition method in type.Methods) {
|
|
|
|
if (method.IsConstructor && method.IsStatic)
|
|
|
|
continue;
|
|
|
|
yield return method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-08-11 11:06:46 +03:00
|
|
|
}
|