Skip to content

Commit

Permalink
[linker] Add AddKeepAlivesStep
Browse files Browse the repository at this point in the history
Other changes:

Move a few methods out of `MonoDroidMarkStep` into `Extensions` so
`AddKeepAlivesStep` can use them too.
  • Loading branch information
brendanzagaeski committed Nov 9, 2020
1 parent 75241fd commit 3e78301
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 94 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Linq;

using Mono.Cecil;

using Java.Interop.Tools.Cecil;

using Mono.Linker;
using Mono.Linker.Steps;

using Mono.Cecil.Cil;

namespace MonoDroid.Tuner
{
public class AddKeepAlivesStep : BaseStep
{
readonly TypeDefinitionCache cache;

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

protected override void ProcessAssembly (AssemblyDefinition assembly)
{
if (AddKeepAlives (assembly)) {
AssemblyAction action = Annotations.HasAction (assembly) ? Annotations.GetAction (assembly) : AssemblyAction.Skip;
if (action == AssemblyAction.Skip || action == AssemblyAction.Copy || action == AssemblyAction.Delete)
Annotations.SetAction (assembly, AssemblyAction.Save);
}
}

internal bool AddKeepAlives (AssemblyDefinition assembly)
{
if (assembly.MainModule.HasTypeReference ("Java.Lang.Object"))
return false;
bool changed = false;
List<TypeDefinition> types = assembly.MainModule.Types.ToList ();
foreach (TypeDefinition type in assembly.MainModule.Types)
AddNestedTypes (types, type);
foreach (TypeDefinition type in types)
if (MightNeedFix (type))
changed |= AddKeepAlives (type);
return changed;
}

// Adapted from `MarkJavaObjects`
static void AddNestedTypes (List<TypeDefinition> types, TypeDefinition type)
{
if (!type.HasNestedTypes)
return;

foreach (var t in type.NestedTypes) {
types.Add (t);
AddNestedTypes (types, t);
}
}

bool MightNeedFix (TypeDefinition type)
{
return !type.IsAbstract && type.IsSubclassOf ("Java.Lang.Object", cache);
}

bool AddKeepAlives (TypeDefinition type)
{
bool changed = false;
foreach (MethodDefinition method in type.Methods) {
if (!method.CustomAttributes.Any (a => a.AttributeType.FullName == "Android.Runtime.RegisterAttribute"))
continue;
ILProcessor processor = method.Body.GetILProcessor ();
ModuleDefinition module = method.DeclaringType.Module;
MethodDefinition methodKeepAlive = Context.GetMethod ("mscorlib", "System.GC", "KeepAlive", new string [] { "System.Object" });
Instruction end = method.Body.Instructions.Last ();
if (end.Previous.OpCode == OpCodes.Endfinally)
end = end.Previous;
for (int i = 0; i < method.Parameters.Count; i++) {
if (method.Parameters [i].ParameterType.IsValueType)
continue;
changed = true;
processor.InsertBefore (end, GetLoadArgumentInstruction (method.IsStatic ? i : i + 1, method.Parameters [i]));
processor.InsertBefore (end, Instruction.Create (OpCodes.Call, module.ImportReference (methodKeepAlive)));
}
}
return changed;
}

// Adapted from src/Mono.Android.Export/Mono.CodeGeneration/CodeArgumentReference.cs
static Instruction GetLoadArgumentInstruction (int argNum, ParameterDefinition parameter)
{
switch (argNum) {
case 0: return Instruction.Create (OpCodes.Ldarg_0);
case 1: return Instruction.Create (OpCodes.Ldarg_1);
case 2: return Instruction.Create (OpCodes.Ldarg_2);
case 3: return Instruction.Create (OpCodes.Ldarg_3);
default: return Instruction.Create (OpCodes.Ldarg, parameter);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,74 @@ public static object GetSettableValue (this CustomAttributeArgument arg)
return td != null ? td.FullName + "," + td.Module.Assembly.FullName : arg.Value;
}

public static AssemblyDefinition GetAssembly (this LinkContext context, string assemblyName)
{
AssemblyDefinition ad;
context.TryGetLinkedAssembly (assemblyName, out ad);
return ad;
}

public static MethodDefinition GetMethod (TypeDefinition td, string name)
{
MethodDefinition method = null;
foreach (var md in td.Methods) {
if (md.Name == name) {
method = md;
break;
}
}

return method;
}

public static MethodDefinition GetMethod (this LinkContext context, string ns, string typeName, string name, string [] parameters)
{
var type = context.GetType (ns, typeName);
if (type == null)
return null;

return GetMethod (type, name, parameters);
}

public static MethodDefinition GetMethod (TypeDefinition type, string name, string [] parameters)
{
MethodDefinition method = null;
foreach (var md in type.Methods) {
if (md.Name != name)
continue;

if (md.Parameters.Count != parameters.Length)
continue;

var equal = true;
for (int i = 0; i < parameters.Length; i++) {
if (md.Parameters [i].ParameterType.FullName != parameters [i]) {
equal = false;
break;
}
}

if (!equal)
continue;

method = md;
break;
}

return method;
}

public static TypeDefinition GetType (this LinkContext context, string assemblyName, string typeName)
{
AssemblyDefinition ad = context.GetAssembly (assemblyName);
return ad == null ? null : GetType (ad, typeName);
}

public static TypeDefinition GetType (AssemblyDefinition assembly, string typeName)
{
return assembly.MainModule.GetType (typeName);
}

public static bool Implements (this TypeReference self, string interfaceName)
{
if (interfaceName == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ static Pipeline CreatePipeline (LinkerOptions options)

if (options.LinkNone) {
pipeline.AppendStep (new FixAbstractMethodsStep (cache));
if (options.AddKeepAlives)
pipeline.AppendStep (new AddKeepAlivesStep (cache));
pipeline.AppendStep (new OutputStepWithTimestamps ());
return pipeline;
}
Expand Down Expand Up @@ -118,6 +120,8 @@ static Pipeline CreatePipeline (LinkerOptions options)
if (!string.IsNullOrWhiteSpace (options.ProguardConfiguration))
pipeline.AppendStep (new GenerateProguardConfiguration (options.ProguardConfiguration));
pipeline.AppendStep (new StripEmbeddedLibraries ());
if (options.AddKeepAlives)
pipeline.AppendStep (new AddKeepAlivesStep (cache));
// end monodroid specific
pipeline.AppendStep (new RegenerateGuidStep ());
pipeline.AppendStep (new OutputStepWithTimestamps ());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class LinkerOptions
public bool DumpDependencies { get; set; }
public string HttpClientHandlerType { get; set; }
public string TlsProvider { get; set; }
public bool AddKeepAlives { get; set; }
public bool PreserveJniMarshalMethods { get; set; }
public bool DeterministicOutput { get; set; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ bool UpdateMarshalTypes ()
var updated = false;

var typeName = "Java.Interop.TypeManager/JavaTypeManager/__<$>_jni_marshal_methods";
var javaTypeManager = GetType ("Mono.Android", typeName);
var javaTypeManager = _context.GetType ("Mono.Android", typeName);
if (javaTypeManager == null)
throw new InvalidOperationException ($"Unable to find '{typeName}' in Mono.Android.dll!");
marshalTypes.Add (javaTypeManager);
Expand Down Expand Up @@ -73,56 +73,6 @@ bool UpdateMarshalTypes ()
return updated;
}

MethodDefinition GetMethod (TypeDefinition td, string name)
{
MethodDefinition method = null;
foreach (var md in td.Methods) {
if (md.Name == name) {
method = md;
break;
}
}

return method;
}

MethodDefinition GetMethod (string ns, string typeName, string name, string[] parameters)
{
var type = GetType (ns, typeName);
if (type == null)
return null;

return GetMethod (type, name, parameters);
}

MethodDefinition GetMethod (TypeDefinition type, string name, string[] parameters)
{
MethodDefinition method = null;
foreach (var md in type.Methods) {
if (md.Name != name)
continue;

if (md.Parameters.Count != parameters.Length)
continue;

var equal = true;
for (int i = 0; i < parameters.Length; i++) {
if (md.Parameters [i].ParameterType.FullName != parameters [i]) {
equal = false;
break;
}
}

if (!equal)
continue;

method = md;
break;
}

return method;
}

MethodReference CreateGenericMethodReference (MethodReference method, GenericInstanceType type)
{
var genericMethod = new MethodReference (method.Name, method.ReturnType) {
Expand All @@ -146,10 +96,10 @@ void UpdateRegistrationSwitch (MethodDefinition method, MethodReference[] switch

instructions.Clear ();

var typeNullable = GetType ("mscorlib", "System.Nullable`1");
var methodGetValueOrDefault = GetMethod (typeNullable, "GetValueOrDefault");
var typeNullable = _context.GetType ("mscorlib", "System.Nullable`1");
var methodGetValueOrDefault = Extensions.GetMethod (typeNullable, "GetValueOrDefault");
var genericTypeNullable = new GenericInstanceType (typeNullable);
genericTypeNullable.GenericArguments.Add (GetType ("mscorlib", "System.Int32"));
genericTypeNullable.GenericArguments.Add (_context.GetType ("mscorlib", "System.Int32"));

var typeIdxVariable = new VariableDefinition (module.ImportReference (genericTypeNullable));
method.Body.Variables.Clear ();
Expand All @@ -165,8 +115,8 @@ void UpdateRegistrationSwitch (MethodDefinition method, MethodReference[] switch
for (var i = 0; i < switchMethods.Length; i++)
switchInstructions [i] = Instruction.Create (OpCodes.Ldtoken, switchMethods [i].DeclaringType);

var typeType = GetType ("mscorlib", "System.Type");
var methodGetTypeFromHandle = GetMethod ("mscorlib", "System.Type", "GetTypeFromHandle", new string[] { "System.RuntimeTypeHandle" });
var typeType = _context.GetType ("mscorlib", "System.Type");
var methodGetTypeFromHandle = _context.GetMethod ("mscorlib", "System.Type", "GetTypeFromHandle", new string[] { "System.RuntimeTypeHandle" });
var callDelegateStart = Instruction.Create (OpCodes.Call, module.ImportReference (methodGetTypeFromHandle));

instructions.Add (Instruction.Create (OpCodes.Switch, switchInstructions));
Expand All @@ -179,19 +129,19 @@ void UpdateRegistrationSwitch (MethodDefinition method, MethodReference[] switch
instructions.Add (Instruction.Create (OpCodes.Ldc_I4_0));
instructions.Add (Instruction.Create (OpCodes.Ret));

var actionType = GetType ("mscorlib", "System.Action`1");
var actionType = _context.GetType ("mscorlib", "System.Action`1");

var genericActionType = new GenericInstanceType (actionType);
var argsType = GetType ("Java.Interop", "Java.Interop.JniNativeMethodRegistrationArguments");
var argsType = _context.GetType ("Java.Interop", "Java.Interop.JniNativeMethodRegistrationArguments");

genericActionType.GenericArguments.Add (argsType);

MarkType (genericActionType);

var actionInvoke = GetMethod (actionType, "Invoke", new string[] { "T" });
var methodGetMethod = GetMethod ("mscorlib", "System.Type", "GetMethod", new string[] { "System.String" });
var typeMethodInfo = GetType ("mscorlib", "System.Reflection.MethodInfo");
var methodCreateDelegate = GetMethod ("mscorlib", "System.Reflection.MethodInfo", "CreateDelegate", new string[] { "System.Type" });
var actionInvoke = Extensions.GetMethod (actionType, "Invoke", new string[] { "T" });
var methodGetMethod = _context.GetMethod ("mscorlib", "System.Type", "GetMethod", new string[] { "System.String" });
var typeMethodInfo = _context.GetType ("mscorlib", "System.Reflection.MethodInfo");
var methodCreateDelegate = _context.GetMethod ("mscorlib", "System.Reflection.MethodInfo", "CreateDelegate", new string[] { "System.Type" });

instructions.Add (callDelegateStart);

Expand Down Expand Up @@ -220,16 +170,16 @@ void UpdateMagicPrefill (TypeDefinition magicType)
if (fieldTypesMap == null)
return;

var methodPrefill = GetMethod (magicType, "Prefill");
var methodPrefill = Extensions.GetMethod (magicType, "Prefill");
if (methodPrefill == null)
return;

var typeDictionary = GetType ("mscorlib", "System.Collections.Generic.Dictionary`2");
var ctorDictionary = GetMethod (typeDictionary, ".ctor", new string[] { "System.Int32" });
var methodSetItem = GetMethod (typeDictionary, "set_Item", new string[] { "TKey", "TValue" });
var typeDictionary = _context.GetType ("mscorlib", "System.Collections.Generic.Dictionary`2");
var ctorDictionary = Extensions.GetMethod (typeDictionary, ".ctor", new string[] { "System.Int32" });
var methodSetItem = Extensions.GetMethod (typeDictionary, "set_Item", new string[] { "TKey", "TValue" });
var genericTypeDictionary = new GenericInstanceType (typeDictionary);
genericTypeDictionary.GenericArguments.Add (GetType ("mscorlib", "System.String"));
genericTypeDictionary.GenericArguments.Add (GetType ("mscorlib", "System.Int32"));
genericTypeDictionary.GenericArguments.Add (_context.GetType ("mscorlib", "System.String"));
genericTypeDictionary.GenericArguments.Add (_context.GetType ("mscorlib", "System.Int32"));

var genericMethodDictionaryCtor = CreateGenericMethodReference (ctorDictionary, genericTypeDictionary);
var genericMethodDictionarySetItem = CreateGenericMethodReference (methodSetItem, genericTypeDictionary);
Expand All @@ -256,19 +206,19 @@ void UpdateMagicPrefill (TypeDefinition magicType)

void UpdateMagicRegistration ()
{
TypeDefinition magicType = GetType ("Mono.Android", "Android.Runtime.AndroidTypeManager/MagicRegistrationMap");
TypeDefinition magicType = _context.GetType ("Mono.Android", "Android.Runtime.AndroidTypeManager/MagicRegistrationMap");
if (magicType == null)
return;

MethodDefinition magicCall = GetMethod (magicType, "CallRegisterMethodByIndex");
MethodDefinition magicCall = Extensions.GetMethod (magicType, "CallRegisterMethodByIndex");
if (magicCall == null)
return;

var switchMethods = new MethodReference [marshalTypes.Count];
var module = magicType.Module;
int idx = 0;
foreach (var type in marshalTypes) {
var md = GetMethod (type, "__RegisterNativeMembers");
var md = Extensions.GetMethod (type, "__RegisterNativeMembers");
if (md == null)
return;

Expand Down Expand Up @@ -594,32 +544,14 @@ void ProcessSystemCore (TypeDefinition type)
}
}

protected AssemblyDefinition GetAssembly (string assemblyName)
{
AssemblyDefinition ad;
_context.TryGetLinkedAssembly (assemblyName, out ad);
return ad;
}

protected TypeDefinition GetType (string assemblyName, string typeName)
{
AssemblyDefinition ad = GetAssembly (assemblyName);
return ad == null ? null : GetType (ad, typeName);
}

protected TypeDefinition GetType (AssemblyDefinition assembly, string typeName)
{
return assembly.MainModule.GetType (typeName);
}

void ProcessSystemData (TypeDefinition type)
{
switch (type.Namespace) {
case "System.Data.SqlTypes":
switch (type.Name) {
case "SqlXml":
// TODO: Needed only if CreateSqlReaderDelegate is used
TypeDefinition xml_reader = GetType ("System.Xml", "System.Xml.XmlReader");
TypeDefinition xml_reader = _context.GetType ("System.Xml", "System.Xml.XmlReader");
MarkNamedMethod (xml_reader, "CreateSqlReader");
break;
}
Expand Down
Loading

0 comments on commit 3e78301

Please sign in to comment.