maui-linux/Xamarin.Forms.Build.Tasks/ModuleDefinitionExtensions.cs

307 строки
17 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Rocks;
namespace Xamarin.Forms.Build.Tasks
{
static class ModuleDefinitionExtensions
{
static Dictionary<(ModuleDefinition module, string typeKey), TypeReference> TypeRefCache = new Dictionary<(ModuleDefinition module, string typeKey), TypeReference>();
public static TypeReference ImportReference(this ModuleDefinition module, (string assemblyName, string clrNamespace, string typeName) type)
{
var typeKey = type.ToString();
if (!TypeRefCache.TryGetValue((module, typeKey), out var typeRef))
TypeRefCache.Add((module, typeKey), typeRef = module.ImportReference(module.GetTypeDefinition(type)));
return typeRef;
}
public static TypeReference ImportReference(this ModuleDefinition module, (string assemblyName, string clrNamespace, string typeName) type, (string assemblyName, string clrNamespace, string typeName)[] classArguments)
{
var typeKey = $"{type}<{string.Join(",",classArguments)}>";
if (!TypeRefCache.TryGetValue((module, typeKey), out var typeRef))
TypeRefCache.Add((module, typeKey), typeRef = module.ImportReference(module.ImportReference(type).MakeGenericInstanceType(classArguments.Select(gp => module.GetTypeDefinition((gp.assemblyName, gp.clrNamespace, gp.typeName))).ToArray())));
return typeRef;
}
public static TypeReference ImportArrayReference(this ModuleDefinition module, (string assemblyName, string clrNamespace, string typeName) type)
{
var typeKey = "${type}[]";
if (!TypeRefCache.TryGetValue((module, typeKey), out var typeRef))
TypeRefCache.Add((module, typeKey), typeRef = module.ImportReference(module.ImportReference(type).MakeArrayType()));
return typeRef;
}
static Dictionary<(ModuleDefinition module, string methodRefKey), MethodReference> MethodRefCache = new Dictionary<(ModuleDefinition module, string methodRefKey), MethodReference>();
static MethodReference ImportCtorReference(this ModuleDefinition module, TypeReference type, TypeReference[] classArguments, Func<MethodDefinition, bool> predicate)
{
var ctor = module.ImportReference(type).ResolveCached().Methods.FirstOrDefault(md => !md.IsPrivate && !md.IsStatic && md.IsConstructor && (predicate?.Invoke(md) ?? true));
if (ctor is null)
return null;
var ctorRef = module.ImportReference(ctor);
if (classArguments == null)
return ctorRef;
return module.ImportReference(ctorRef.ResolveGenericParameters(type.MakeGenericInstanceType(classArguments), module));
}
public static MethodReference ImportCtorReference(this ModuleDefinition module, TypeReference type, TypeReference[] parameterTypes)
{
var ctorKey = $"{type}.ctor({(parameterTypes == null ? "" : string.Join(",", parameterTypes.Select(tr => (tr.Scope.Name, tr.Namespace, tr.Name))))})";
if (MethodRefCache.TryGetValue((module, ctorKey), out var ctorRef))
return ctorRef;
ctorRef = module.ImportCtorReference(type, classArguments: null, predicate: md => {
if (md.Parameters.Count != (parameterTypes?.Length ?? 0))
return false;
for (var i = 0; i < md.Parameters.Count; i++)
if (!TypeRefComparer.Default.Equals(md.Parameters[i].ParameterType, module.ImportReference(parameterTypes[i])))
return false;
return true;
});
MethodRefCache.Add((module, ctorKey), ctorRef);
return ctorRef;
}
public static MethodReference ImportCtorReference(this ModuleDefinition module, (string assemblyName, string clrNamespace, string typeName) type, int paramCount)
{
var ctorKey = $"{type}.ctor({(string.Join(",", Enumerable.Repeat("_", paramCount)))})";
if (!MethodRefCache.TryGetValue((module, ctorKey), out var ctorRef))
MethodRefCache.Add((module, ctorKey), ctorRef = module.ImportCtorReference(module.GetTypeDefinition(type), null, md => md.Parameters.Count == paramCount));
return ctorRef;
}
public static MethodReference ImportCtorReference(this ModuleDefinition module, TypeReference type, int paramCount)
{
var ctorKey = $"{type}.ctor({(string.Join(",", Enumerable.Repeat("_", paramCount)))})";
if (!MethodRefCache.TryGetValue((module, ctorKey), out var ctorRef))
MethodRefCache.Add((module, ctorKey), ctorRef = module.ImportCtorReference(type, null, md => md.Parameters.Count == paramCount));
return ctorRef;
}
public static MethodReference ImportCtorReference(this ModuleDefinition module, (string assemblyName, string clrNamespace, string typeName) type, int paramCount, (string assemblyName, string clrNamespace, string typeName)[] classArguments)
{
var ctorKey = $"{type}<{(string.Join(",", classArguments))}>.ctor({(string.Join(",", Enumerable.Repeat("_", paramCount)))})";
if (!MethodRefCache.TryGetValue((module, ctorKey), out var ctorRef))
MethodRefCache.Add((module, ctorKey), ctorRef = module.ImportCtorReference(module.GetTypeDefinition(type), classArguments.Select(module.GetTypeDefinition).ToArray(), md=>md.Parameters.Count==paramCount));
return ctorRef;
}
public static MethodReference ImportCtorReference(this ModuleDefinition module, (string assemblyName, string clrNamespace, string typeName) type, int paramCount, TypeReference[] classArguments)
{
var ctorKey = $"{type}<{(string.Join(",", classArguments.Select(tr => (tr.Scope.Name, tr.Namespace, tr.Name))))}>.ctor({(string.Join(",", Enumerable.Repeat("_", paramCount)))})";
if (!MethodRefCache.TryGetValue((module, ctorKey), out var ctorRef))
MethodRefCache.Add((module, ctorKey), ctorRef = module.ImportCtorReference(module.GetTypeDefinition(type), classArguments, predicate: md => md.Parameters.Count == paramCount));
return ctorRef;
}
public static MethodReference ImportCtorReference(this ModuleDefinition module, (string assemblyName, string clrNamespace, string typeName) type, (string assemblyName, string clrNamespace, string typeName)[] parameterTypes, (string assemblyName, string clrNamespace, string typeName)[] classArguments)
{
var ctorKey = $"{type}<{(string.Join(",", classArguments))}>.ctor({(parameterTypes == null ? "" : string.Join(",", parameterTypes))})";
if (MethodRefCache.TryGetValue((module, ctorKey), out var ctorRef))
return ctorRef;
ctorRef = module.ImportCtorReference(module.GetTypeDefinition(type), classArguments.Select(module.GetTypeDefinition).ToArray(), md => {
if (md.Parameters.Count != (parameterTypes?.Length ?? 0))
return false;
for (var i = 0; i < md.Parameters.Count; i++)
if (!TypeRefComparer.Default.Equals(md.Parameters[i].ParameterType, module.ImportReference(parameterTypes[i])))
return false;
return true;
});
MethodRefCache.Add((module, ctorKey), ctorRef);
return ctorRef;
}
public static MethodReference ImportCtorReference(this ModuleDefinition module, (string assemblyName, string clrNamespace, string typeName) type, (string assemblyName, string clrNamespace, string typeName)[] parameterTypes)
{
var ctorKey = $"{type}.ctor({(parameterTypes == null ? "" : string.Join(",", parameterTypes))})";
if (MethodRefCache.TryGetValue((module, ctorKey), out var ctorRef))
return ctorRef;
ctorRef = module.ImportCtorReference(module.GetTypeDefinition(type), classArguments: null, predicate: md => {
if (md.Parameters.Count != (parameterTypes?.Length ?? 0))
return false;
for (var i = 0; i < md.Parameters.Count; i++)
if (!TypeRefComparer.Default.Equals(md.Parameters[i].ParameterType, module.ImportReference(parameterTypes[i])))
return false;
return true;
});
MethodRefCache.Add((module, ctorKey), ctorRef);
return ctorRef;
}
static MethodReference ImportPropertyGetterReference(this ModuleDefinition module, TypeReference type, string propertyName, Func<PropertyDefinition, bool> predicate = null, bool flatten = false, bool caseSensitive = true)
{
var properties = module.ImportReference(type).Resolve().Properties;
var getter = module
.ImportReference(type)
.ResolveCached()
.Properties(flatten)
.FirstOrDefault(pd =>
string.Equals(pd.Name, propertyName, caseSensitive ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase)
&& !pd.GetMethod.IsPrivate
&& (predicate?.Invoke(pd) ?? true))
?.GetMethod;
return getter == null ? null : module.ImportReference(getter);
}
public static MethodReference ImportPropertyGetterReference(this ModuleDefinition module, (string assemblyName, string clrNamespace, string typeName) type, string propertyName, bool isStatic = false, bool flatten = false, bool caseSensitive = true)
{
var getterKey = $"{(isStatic ? "static " : "")}{type}.get_{propertyName}{(flatten ? "*" : "")}";
if (!MethodRefCache.TryGetValue((module, getterKey), out var methodReference))
MethodRefCache.Add((module, getterKey), methodReference = module.ImportPropertyGetterReference(module.GetTypeDefinition(type), propertyName, pd => pd.GetMethod.IsStatic == isStatic, flatten, caseSensitive: caseSensitive));
return methodReference;
}
static MethodReference ImportPropertySetterReference(this ModuleDefinition module, TypeReference type, string propertyName, Func<PropertyDefinition, bool> predicate = null)
{
var setter = module
.ImportReference(type)
.ResolveCached()
.Properties
.FirstOrDefault(pd =>
pd.Name == propertyName
&& !pd.SetMethod.IsPrivate
&& (predicate?.Invoke(pd) ?? true))
?.SetMethod;
return setter == null ? null : module.ImportReference(setter);
}
public static MethodReference ImportPropertySetterReference(this ModuleDefinition module, (string assemblyName, string clrNamespace, string typeName) type, string propertyName, bool isStatic = false)
{
var setterKey = $"{(isStatic ? "static " : "")}{type}.set{propertyName}";
if (!MethodRefCache.TryGetValue((module, setterKey), out var methodReference))
MethodRefCache.Add((module,setterKey), methodReference = module.ImportPropertySetterReference(module.GetTypeDefinition(type), propertyName, pd => pd.SetMethod.IsStatic == isStatic));
return methodReference;
}
static MethodReference ImportMethodReference(this ModuleDefinition module, TypeReference type, string methodName, Func<MethodDefinition, bool> predicate = null, TypeReference[] classArguments = null)
{
var method = module
.ImportReference(type)
.ResolveCached()
.Methods
.FirstOrDefault(md =>
!md.IsConstructor
&& !md.IsPrivate
&& md.Name == methodName
&& (predicate?.Invoke(md) ?? true));
if (method is null)
return null;
var methodRef = module.ImportReference(method);
if (classArguments == null)
return methodRef;
return module.ImportReference(methodRef.ResolveGenericParameters(type.MakeGenericInstanceType(classArguments), module));
}
public static MethodReference ImportMethodReference(this ModuleDefinition module,
(string assemblyName, string clrNamespace, string typeName) type,
string methodName,
(string assemblyName, string clrNamespace, string typeName)[] parameterTypes,
(string assemblyName, string clrNamespace, string typeName)[] classArguments = null,
bool isStatic = false)
{
var methodKey = $"{(isStatic ? "static " : "")}{type}<{(classArguments == null ? "" : string.Join(",", classArguments))}>.({(parameterTypes == null ? "" : string.Join(",", parameterTypes))})";
if (MethodRefCache.TryGetValue((module, methodKey), out var methodReference))
return methodReference;
methodReference = module.ImportMethodReference(module.GetTypeDefinition(type),
methodName: methodName,
predicate: md => {
if (md.IsStatic != isStatic)
return false;
if (md.Parameters.Count != (parameterTypes?.Length ?? 0))
return false;
for (var i = 0; i < md.Parameters.Count; i++)
if (!TypeRefComparer.Default.Equals(md.Parameters[i].ParameterType, module.ImportReference(parameterTypes[i])))
return false;
return true;
},
classArguments: classArguments?.Select(gp => module.GetTypeDefinition((gp.assemblyName, gp.clrNamespace, gp.typeName))).ToArray());
MethodRefCache.Add((module, methodKey), methodReference);
return methodReference;
}
public static MethodReference ImportMethodReference(this ModuleDefinition module,
(string assemblyName, string clrNamespace, string typeName) type,
string methodName,
int paramCount,
(string assemblyName, string clrNamespace, string typeName)[] classArguments = null,
bool isStatic = false)
{
var methodKey = $"{(isStatic ? "static " : "")}{type}<{(classArguments == null ? "" : string.Join(",", classArguments))}>.({(string.Join(",", Enumerable.Repeat("_", paramCount)))})";
if (MethodRefCache.TryGetValue((module, methodKey), out var methodReference))
return methodReference;
methodReference = module.ImportMethodReference(module.GetTypeDefinition(type),
methodName: methodName,
predicate: md => {
if (md.IsStatic != isStatic)
return false;
if (md.Parameters.Count != paramCount)
return false;
return true;
},
classArguments: classArguments?.Select(gp => module.GetTypeDefinition((gp.assemblyName, gp.clrNamespace, gp.typeName))).ToArray());
MethodRefCache.Add((module, methodKey), methodReference);
return methodReference;
}
static Dictionary<(ModuleDefinition module, string fieldRefKey), FieldReference> FieldRefCache = new Dictionary<(ModuleDefinition module, string fieldRefKey), FieldReference>();
static FieldReference ImportFieldReference(this ModuleDefinition module, TypeReference type, string fieldName, Func<FieldDefinition, bool> predicate = null, bool caseSensitive = true)
{
var field = module
.ImportReference(type)
.ResolveCached()
.Fields
.FirstOrDefault(fd =>
string.Equals(fd.Name, fieldName, caseSensitive ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase)
&& (predicate?.Invoke(fd) ?? true));
return field == null ? null : module.ImportReference(field);
}
public static FieldReference ImportFieldReference(this ModuleDefinition module, (string assemblyName, string clrNamespace, string typeName) type, string fieldName, bool isStatic = false, bool caseSensitive = true)
{
var fieldKey = $"{(isStatic ? "static " : "")}{type}.{(caseSensitive ? fieldName : fieldName.ToLowerInvariant())}";
if (!FieldRefCache.TryGetValue((module, fieldKey), out var fieldReference))
FieldRefCache.Add((module, fieldKey), fieldReference = module.ImportFieldReference(module.GetTypeDefinition(type), fieldName: fieldName, predicate: fd => fd.IsStatic == isStatic, caseSensitive: caseSensitive));
return fieldReference;
}
static Dictionary<(ModuleDefinition module, (string assemblyName, string clrNamespace, string typeName)), TypeDefinition> typeDefCache
= new Dictionary<(ModuleDefinition module, (string assemblyName, string clrNamespace, string typeName)), TypeDefinition>();
public static TypeDefinition GetTypeDefinition(this ModuleDefinition module, (string assemblyName, string clrNamespace, string typeName) type)
{
if (typeDefCache.TryGetValue((module, type), out TypeDefinition cachedTypeDefinition))
return cachedTypeDefinition;
var asm = module.Assembly.Name.Name == type.assemblyName
? module.Assembly
: module.AssemblyResolver.Resolve(AssemblyNameReference.Parse(type.assemblyName));
var typeDef = asm.MainModule.GetType($"{type.clrNamespace}.{type.typeName}");
if (typeDef != null) {
typeDefCache.Add((module, type), typeDef);
return typeDef;
}
var exportedType = asm.MainModule.ExportedTypes.FirstOrDefault(
arg => arg.IsForwarder && arg.Namespace == type.clrNamespace && arg.Name == type.typeName);
if (exportedType != null) {
typeDef = exportedType.Resolve();
typeDefCache.Add((module, type), typeDef);
return typeDef;
}
//I hate you, netstandard
if (type.assemblyName == "mscorlib" && type.clrNamespace == "System.Reflection")
return module.GetTypeDefinition(("System.Reflection", type.clrNamespace, type.typeName));
return null;
}
static IEnumerable<PropertyDefinition> Properties(this TypeDefinition typedef, bool flatten)
{
foreach (var property in typedef.Properties)
yield return property;
if (!flatten || typedef.BaseType == null)
yield break;
foreach (var property in typedef.BaseType.ResolveCached().Properties(true))
yield return property;
}
}
}