1660 lines
77 KiB
C#
1660 lines
77 KiB
C#
using Mono.Cecil;
|
|
using Mono.Cecil.Cil;
|
|
using Mono.Collections.Generic;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
|
|
#if NETSTANDARD
|
|
using TypeOrTypeInfo = System.Reflection.TypeInfo;
|
|
using static System.Reflection.IntrospectionExtensions;
|
|
using static System.Reflection.TypeExtensions;
|
|
#else
|
|
using TypeOrTypeInfo = System.Type;
|
|
#endif
|
|
|
|
namespace MonoMod.Utils {
|
|
[MonoMod__OldName__("MonoMod.Relinker")]
|
|
public delegate IMetadataTokenProvider Relinker(IMetadataTokenProvider mtp, IGenericParameterProvider context);
|
|
|
|
/// <summary>
|
|
/// Huge collection of MonoMod-related Mono.Cecil extensions.
|
|
/// </summary>
|
|
[MonoMod__OldName__("MonoMod.MonoModExt")]
|
|
public static class MonoModExt {
|
|
|
|
public static Dictionary<string, object> SharedData = new Dictionary<string, object>();
|
|
|
|
static readonly Regex TypeGenericParamRegex = new Regex(@"\!\d");
|
|
static readonly Regex MethodGenericParamRegex = new Regex(@"\!\!\d");
|
|
|
|
static readonly Type t_ParamArrayAttribute = typeof(ParamArrayAttribute);
|
|
|
|
public static ModuleDefinition ReadModule(string path, ReaderParameters rp) {
|
|
Retry:
|
|
try {
|
|
return ModuleDefinition.ReadModule(path, rp);
|
|
} catch {
|
|
if (rp.ReadSymbols) {
|
|
rp.ReadSymbols = false;
|
|
goto Retry;
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public static ModuleDefinition ReadModule(Stream input, ReaderParameters rp) {
|
|
Retry:
|
|
try {
|
|
return ModuleDefinition.ReadModule(input, rp);
|
|
} catch {
|
|
if (rp.ReadSymbols) {
|
|
rp.ReadSymbols = false;
|
|
goto Retry;
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public static MethodDefinition Clone(this MethodDefinition o, MethodDefinition c = null) {
|
|
if (o == null)
|
|
return null;
|
|
if (c == null)
|
|
c = new MethodDefinition(o.Name, o.Attributes, o.ReturnType);
|
|
c.Name = o.Name;
|
|
c.Attributes = o.Attributes;
|
|
c.ReturnType = o.ReturnType;
|
|
c.DeclaringType = o.DeclaringType;
|
|
c.MetadataToken = c.MetadataToken;
|
|
c.Body = o.Body.Clone(c);
|
|
c.Attributes = o.Attributes;
|
|
c.ImplAttributes = o.ImplAttributes;
|
|
c.PInvokeInfo = o.PInvokeInfo;
|
|
c.IsPreserveSig = o.IsPreserveSig;
|
|
c.IsPInvokeImpl = o.IsPInvokeImpl;
|
|
|
|
foreach (GenericParameter genParam in o.GenericParameters)
|
|
c.GenericParameters.Add(genParam.Clone());
|
|
|
|
foreach (ParameterDefinition param in o.Parameters)
|
|
c.Parameters.Add(param);
|
|
|
|
foreach (CustomAttribute attrib in o.CustomAttributes)
|
|
c.CustomAttributes.Add(attrib.Clone());
|
|
|
|
foreach (MethodReference @override in o.Overrides)
|
|
c.Overrides.Add(@override);
|
|
|
|
return c;
|
|
}
|
|
|
|
public static MethodBody Clone(this MethodBody bo, MethodDefinition m) {
|
|
if (bo == null)
|
|
return null;
|
|
|
|
MethodBody bc = new MethodBody(m);
|
|
bc.MaxStackSize = bo.MaxStackSize;
|
|
bc.InitLocals = bo.InitLocals;
|
|
bc.LocalVarToken = bo.LocalVarToken;
|
|
|
|
bc.Instructions.AddRange(bo.Instructions.Select(o => {
|
|
Instruction c = Instruction.Create(OpCodes.Nop);
|
|
c.OpCode = o.OpCode;
|
|
c.Operand = o.Operand;
|
|
c.Offset = o.Offset;
|
|
return c;
|
|
}));
|
|
|
|
foreach (Instruction c in bc.Instructions) {
|
|
if (c.Operand is Instruction target) {
|
|
c.Operand = bc.Instructions[bo.Instructions.IndexOf(target)];
|
|
} else if (c.Operand is Instruction[] targets) {
|
|
c.Operand = targets.Select(i => bc.Instructions[bo.Instructions.IndexOf(i)]).ToArray();
|
|
}
|
|
}
|
|
|
|
bc.ExceptionHandlers.AddRange(bo.ExceptionHandlers.Select(o => {
|
|
ExceptionHandler c = new ExceptionHandler(o.HandlerType);
|
|
c.TryStart = o.TryStart == null ? null : bc.Instructions[bo.Instructions.IndexOf(o.TryStart)];
|
|
c.TryEnd = o.TryEnd == null ? null : bc.Instructions[bo.Instructions.IndexOf(o.TryEnd)];
|
|
c.FilterStart = o.FilterStart == null ? null : bc.Instructions[bo.Instructions.IndexOf(o.FilterStart)];
|
|
c.HandlerStart = o.HandlerStart == null ? null : bc.Instructions[bo.Instructions.IndexOf(o.HandlerStart)];
|
|
c.HandlerEnd = o.HandlerEnd == null ? null : bc.Instructions[bo.Instructions.IndexOf(o.HandlerEnd)];
|
|
c.CatchType = o.CatchType;
|
|
return c;
|
|
}));
|
|
|
|
bc.Variables.AddRange(bo.Variables.Select(o => {
|
|
VariableDefinition c = new VariableDefinition(o.VariableType);
|
|
return c;
|
|
}));
|
|
|
|
#if !CECIL0_9
|
|
m.CustomDebugInformations.AddRange(bo.Method.CustomDebugInformations); // Abstract. TODO: Implement deep CustomDebugInformations copy.
|
|
m.DebugInformation.SequencePoints.AddRange(bo.Method.DebugInformation.SequencePoints.Select(o => {
|
|
SequencePoint c = new SequencePoint(bc.Instructions.FirstOrDefault(i => i.Offset == o.Offset), o.Document);
|
|
c.StartLine = o.StartLine;
|
|
c.StartColumn = o.StartColumn;
|
|
c.EndLine = o.EndLine;
|
|
c.EndColumn = o.EndColumn;
|
|
return c;
|
|
}));
|
|
#endif
|
|
|
|
return bc;
|
|
}
|
|
|
|
public static readonly System.Reflection.FieldInfo f_GenericParameter_position = typeof(GenericParameter).GetField("position", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
|
public static readonly System.Reflection.FieldInfo f_GenericParameter_type = typeof(GenericParameter).GetField("type", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
|
public static GenericParameter Update(this GenericParameter param, GenericParameter other)
|
|
=> param.Update(other.Position, other.Type);
|
|
public static GenericParameter Update(this GenericParameter param, int position, GenericParameterType type) {
|
|
f_GenericParameter_position.SetValue(param, position);
|
|
f_GenericParameter_type.SetValue(param, type);
|
|
return param;
|
|
}
|
|
|
|
public static void AddAttribute(this ICustomAttributeProvider cap, MethodReference constructor)
|
|
=> cap.AddAttribute(new CustomAttribute(constructor));
|
|
public static void AddAttribute(this ICustomAttributeProvider cap, CustomAttribute attr)
|
|
=> cap.CustomAttributes.Add(attr);
|
|
|
|
/// <summary>
|
|
/// Determines if the attribute provider has got a specific MonoMod attribute.
|
|
/// </summary>
|
|
/// <returns><c>true</c> if the attribute provider contains the given MonoMod attribute, <c>false</c> otherwise.</returns>
|
|
/// <param name="cap">Attribute provider to check.</param>
|
|
/// <param name="attribute">Attribute.</param>
|
|
public static bool HasMMAttribute(this ICustomAttributeProvider cap, string attribute)
|
|
=> cap.HasCustomAttribute("MonoMod.MonoMod" + attribute);
|
|
public static CustomAttribute GetMMAttribute(this ICustomAttributeProvider cap, string attribute)
|
|
=> cap.GetCustomAttribute("MonoMod.MonoMod" + attribute);
|
|
public static CustomAttribute GetNextMMAttribute(this ICustomAttributeProvider cap, string attribute)
|
|
=> cap.GetNextCustomAttribute("MonoMod.MonoMod" + attribute);
|
|
|
|
public static CustomAttribute GetCustomAttribute(this ICustomAttributeProvider cap, string attribute) {
|
|
if (cap == null || !cap.HasCustomAttributes) return null;
|
|
foreach (CustomAttribute attrib in cap.CustomAttributes)
|
|
if (attrib.AttributeType.FullName == attribute)
|
|
return attrib;
|
|
return null;
|
|
}
|
|
|
|
public static CustomAttribute GetNextCustomAttribute(this ICustomAttributeProvider cap, string attribute) {
|
|
if (cap == null || !cap.HasCustomAttributes) return null;
|
|
bool next = false;
|
|
for (int i = 0; i < cap.CustomAttributes.Count; i++) {
|
|
CustomAttribute attrib = cap.CustomAttributes[i];
|
|
if (attrib.AttributeType.FullName != attribute)
|
|
continue;
|
|
if (!next) {
|
|
cap.CustomAttributes.RemoveAt(i);
|
|
i--;
|
|
next = true;
|
|
continue;
|
|
}
|
|
return attrib;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the attribute provider has got a specific custom attribute.
|
|
/// </summary>
|
|
/// <returns><c>true</c> if the attribute provider contains the given custom attribute, <c>false</c> otherwise.</returns>
|
|
/// <param name="cap">Attribute provider to check.</param>
|
|
/// <param name="attribute">Attribute.</param>
|
|
public static bool HasCustomAttribute(this ICustomAttributeProvider cap, string attribute)
|
|
=> cap.GetCustomAttribute(attribute) != null;
|
|
|
|
public static string GetOriginalName(this MethodDefinition method) {
|
|
foreach (CustomAttribute attrib in method.CustomAttributes)
|
|
if (attrib.AttributeType.FullName == "MonoMod.MonoModOriginalName")
|
|
return (string) attrib.ConstructorArguments[0].Value;
|
|
|
|
if (method.Name == ".ctor" || method.Name == ".cctor") {
|
|
return "orig_ctor_" + ((MemberReference) method.DeclaringType).GetPatchName();
|
|
}
|
|
|
|
return "orig_" + method.Name;
|
|
}
|
|
|
|
public static bool MatchingConditionals(this ICustomAttributeProvider cap, ModuleDefinition module)
|
|
=> cap.MatchingConditionals(module.Assembly.Name);
|
|
public static bool MatchingConditionals(this ICustomAttributeProvider cap, AssemblyNameReference asmName = null) {
|
|
if (cap == null) return true;
|
|
if (!cap.HasCustomAttributes) return true;
|
|
Platform plat = PlatformHelper.Current;
|
|
bool status = true;
|
|
foreach (CustomAttribute attrib in cap.CustomAttributes) {
|
|
if (attrib.AttributeType.FullName == "MonoMod.MonoModOnPlatform") {
|
|
CustomAttributeArgument[] plats = (CustomAttributeArgument[]) attrib.ConstructorArguments[0].Value;
|
|
for (int i = 0; i < plats.Length; i++) {
|
|
if (PlatformHelper.Is((Platform) plats[i].Value)) {
|
|
// status &= true;
|
|
continue;
|
|
}
|
|
}
|
|
status &= plats.Length == 0;
|
|
continue;
|
|
}
|
|
|
|
if (attrib.AttributeType.FullName == "MonoMod.MonoModIfFlag") {
|
|
string flag = (string) attrib.ConstructorArguments[0].Value;
|
|
bool value;
|
|
if (!SharedData.TryGetValue(flag, out object valueObj) || !(valueObj is bool))
|
|
if (attrib.ConstructorArguments.Count == 2)
|
|
value = (bool) attrib.ConstructorArguments[1].Value;
|
|
else
|
|
value = true;
|
|
else
|
|
value = (bool) valueObj;
|
|
status &= value;
|
|
continue;
|
|
}
|
|
|
|
if (attrib.AttributeType.FullName == "MonoMod.MonoModTargetModule") {
|
|
string name = ((string) attrib.ConstructorArguments[0].Value).Inject(SharedData);
|
|
status &= asmName.Name == name || asmName.FullName == name;
|
|
continue;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
public static string GetFindableID(this MethodReference method, string name = null, string type = null, bool withType = true, bool simple = false) {
|
|
while (method.IsGenericInstance)
|
|
method = ((GenericInstanceMethod) method).ElementMethod;
|
|
|
|
StringBuilder builder = new StringBuilder();
|
|
|
|
if (simple) {
|
|
if (withType && method.DeclaringType != null)
|
|
builder.Append(type ?? method.DeclaringType.GetPatchFullName()).Append("::");
|
|
builder.Append(name ?? method.Name);
|
|
return builder.ToString();
|
|
}
|
|
|
|
builder
|
|
.Append(method.ReturnType.GetPatchFullName())
|
|
.Append(" ");
|
|
|
|
if (withType)
|
|
builder.Append(type ?? method.DeclaringType.GetPatchFullName()).Append("::");
|
|
|
|
builder
|
|
.Append(name ?? method.Name);
|
|
|
|
if (method.GenericParameters.Count != 0) {
|
|
builder.Append("<");
|
|
Collection<GenericParameter> arguments = method.GenericParameters;
|
|
for (int i = 0; i < arguments.Count; i++) {
|
|
if (i > 0)
|
|
builder.Append(",");
|
|
builder.Append(arguments[i].Name);
|
|
}
|
|
builder.Append(">");
|
|
}
|
|
|
|
builder.Append("(");
|
|
|
|
if (method.HasParameters) {
|
|
Collection<ParameterDefinition> parameters = method.Parameters;
|
|
for (int i = 0; i < parameters.Count; i++) {
|
|
ParameterDefinition parameter = parameters[i];
|
|
if (i > 0)
|
|
builder.Append(",");
|
|
|
|
if (parameter.ParameterType.IsSentinel)
|
|
builder.Append("...,");
|
|
|
|
builder.Append(parameter.ParameterType.GetPatchFullName());
|
|
}
|
|
}
|
|
|
|
builder.Append(")");
|
|
|
|
return builder.ToString();
|
|
}
|
|
|
|
public static string GetFindableID(this CallSite method) {
|
|
StringBuilder builder = new StringBuilder();
|
|
|
|
builder
|
|
.Append(method.ReturnType.GetPatchFullName())
|
|
.Append(" ");
|
|
|
|
builder.Append("(");
|
|
|
|
if (method.HasParameters) {
|
|
Collection<ParameterDefinition> parameters = method.Parameters;
|
|
for (int i = 0; i < parameters.Count; i++) {
|
|
ParameterDefinition parameter = parameters[i];
|
|
if (i > 0)
|
|
builder.Append(",");
|
|
|
|
if (parameter.ParameterType.IsSentinel)
|
|
builder.Append("...,");
|
|
|
|
builder.Append(parameter.ParameterType.GetPatchFullName());
|
|
}
|
|
}
|
|
|
|
builder.Append(")");
|
|
|
|
return builder.ToString();
|
|
}
|
|
|
|
public static string GetFindableID(this System.Reflection.MethodBase method, string name = null, string type = null, bool withType = true, bool proxyMethod = false, bool simple = false) {
|
|
while (method is System.Reflection.MethodInfo && method.IsGenericMethod && !method.IsGenericMethodDefinition)
|
|
method = ((System.Reflection.MethodInfo) method).GetGenericMethodDefinition();
|
|
|
|
StringBuilder builder = new StringBuilder();
|
|
|
|
if (simple) {
|
|
if (withType && method.DeclaringType != null)
|
|
builder.Append(type ?? method.DeclaringType.FullName).Append("::");
|
|
builder.Append(name ?? method.Name);
|
|
return builder.ToString();
|
|
}
|
|
|
|
builder
|
|
.Append((method as System.Reflection.MethodInfo)?.ReturnType?.FullName ?? "System.Void")
|
|
.Append(" ");
|
|
|
|
if (withType)
|
|
builder.Append(type ?? method.DeclaringType.FullName.Replace("+", "/")).Append("::");
|
|
|
|
builder
|
|
.Append(name ?? method.Name);
|
|
|
|
if (method.ContainsGenericParameters) {
|
|
builder.Append("<");
|
|
Type[] arguments = method.GetGenericArguments();
|
|
for (int i = 0; i < arguments.Length; i++) {
|
|
if (i > 0)
|
|
builder.Append(",");
|
|
builder.Append(arguments[i].Name);
|
|
}
|
|
builder.Append(">");
|
|
}
|
|
|
|
builder.Append("(");
|
|
|
|
System.Reflection.ParameterInfo[] parameters = method.GetParameters();
|
|
for (int i = proxyMethod ? 1 : 0; i < parameters.Length; i++) {
|
|
System.Reflection.ParameterInfo parameter = parameters[i];
|
|
if (i > (proxyMethod ? 1 : 0))
|
|
builder.Append(",");
|
|
|
|
#if NETSTANDARD
|
|
if (System.Reflection.CustomAttributeExtensions.IsDefined(parameter, t_ParamArrayAttribute, false))
|
|
#else
|
|
if (t_ParamArrayAttribute.GetCustomAttributes(t_ParamArrayAttribute, false).Length != 0)
|
|
#endif
|
|
builder.Append("...,");
|
|
|
|
builder.Append(parameter.ParameterType.FullName);
|
|
}
|
|
|
|
builder.Append(")");
|
|
|
|
return builder.ToString();
|
|
}
|
|
|
|
public static bool Is(this System.Reflection.MemberInfo minfo, MemberReference mref)
|
|
=> mref.Is(minfo);
|
|
public static bool Is(this MemberReference mref, System.Reflection.MemberInfo minfo) {
|
|
if (mref == null)
|
|
return false;
|
|
|
|
TypeReference mrefDecl = mref.DeclaringType;
|
|
if (mrefDecl?.FullName == "<Module>")
|
|
mrefDecl = null;
|
|
|
|
if (mref is GenericParameter genParamRef) {
|
|
if (!(minfo is TypeOrTypeInfo genParamInfo) || !genParamInfo.IsGenericParameter)
|
|
return false;
|
|
// Don't check owner as it introduces a circular check.
|
|
/*
|
|
if (!(genParamRef.Owner as MemberReference).Is(genParamInfo.DeclaringMethod ?? (System.Reflection.MemberInfo) genParamInfo.DeclaringType))
|
|
return false;
|
|
*/
|
|
return genParamRef.Position == genParamInfo.GenericParameterPosition;
|
|
}
|
|
|
|
if (!(mrefDecl?.Is(minfo.DeclaringType?.GetTypeInfo()) ?? minfo.DeclaringType == null))
|
|
return false;
|
|
// Note: This doesn't work for TypeSpecification, as the reflection-side type.Name changes according to any modifiers (f.e. IsArray).
|
|
if (!(mref is TypeSpecification) && mref.Name != minfo.Name)
|
|
return false;
|
|
|
|
if (mref is TypeReference typeRef) {
|
|
if (!(minfo is TypeOrTypeInfo typeInfo))
|
|
return false;
|
|
|
|
if (mref is GenericInstanceType genTypeRef) {
|
|
if (!typeInfo.IsGenericType)
|
|
return false;
|
|
|
|
Collection<TypeReference> gparamRefs = genTypeRef.GenericArguments;
|
|
Type[] gparamInfos = typeInfo.AsType().GetGenericArguments();
|
|
if (gparamRefs.Count != gparamInfos.Length)
|
|
return false;
|
|
|
|
for (int i = 0; i < gparamRefs.Count; i++) {
|
|
if (!gparamRefs[i].Is(gparamInfos[i].GetTypeInfo()))
|
|
return false;
|
|
}
|
|
|
|
return genTypeRef.ElementType.Is(typeInfo.GetGenericTypeDefinition().GetTypeInfo());
|
|
|
|
} else if (typeRef.HasGenericParameters) {
|
|
if (!typeInfo.IsGenericType)
|
|
return false;
|
|
|
|
Collection<GenericParameter> gparamRefs = typeRef.GenericParameters;
|
|
Type[] gparamInfos = typeInfo.AsType().GetGenericArguments();
|
|
if (gparamRefs.Count != gparamInfos.Length)
|
|
return false;
|
|
|
|
for (int i = 0; i < gparamRefs.Count; i++) {
|
|
if (!gparamRefs[i].Is(gparamInfos[i].GetTypeInfo()))
|
|
return false;
|
|
}
|
|
|
|
} else if (typeInfo.IsGenericType)
|
|
return false;
|
|
|
|
if (mref is ArrayType arrayTypeRef) {
|
|
if (!typeInfo.IsArray)
|
|
return false;
|
|
|
|
return arrayTypeRef.Dimensions.Count == typeInfo.GetArrayRank() && arrayTypeRef.ElementType.Is(typeInfo.GetElementType().GetTypeInfo());
|
|
}
|
|
|
|
if (mref is TypeSpecification typeSpecRef)
|
|
// Note: There are TypeSpecifications which map to non-ElementType-y reflection Types.
|
|
return typeSpecRef.ElementType.Is(typeInfo.HasElementType ? typeInfo.GetElementType().GetTypeInfo() : typeInfo);
|
|
|
|
// DeclaringType was already checked before.
|
|
// Avoid converting nested type separators between + (.NET) and / (cecil)
|
|
if (mrefDecl != null)
|
|
return mref.Name == typeInfo.Name;
|
|
return mref.FullName == typeInfo.FullName;
|
|
}
|
|
|
|
if (mref is MethodReference methodRef) {
|
|
if (!(minfo is System.Reflection.MethodBase methodInfo))
|
|
return false;
|
|
|
|
Collection<ParameterDefinition> paramRefs = methodRef.Parameters;
|
|
System.Reflection.ParameterInfo[] paramInfos = methodInfo.GetParameters();
|
|
if (paramRefs.Count != paramInfos.Length)
|
|
return false;
|
|
|
|
if (mref is GenericInstanceMethod genMethodRef) {
|
|
if (!methodInfo.IsGenericMethod)
|
|
return false;
|
|
|
|
Collection<TypeReference> gparamRefs = genMethodRef.GenericArguments;
|
|
Type[] gparamInfos = methodInfo.GetGenericArguments();
|
|
if (gparamRefs.Count != gparamInfos.Length)
|
|
return false;
|
|
|
|
for (int i = 0; i < gparamRefs.Count; i++) {
|
|
if (!gparamRefs[i].Is(gparamInfos[i].GetTypeInfo()))
|
|
return false;
|
|
}
|
|
|
|
return genMethodRef.ElementMethod.Is((methodInfo as System.Reflection.MethodInfo)?.GetGenericMethodDefinition() ?? methodInfo);
|
|
|
|
} else if (methodRef.HasGenericParameters) {
|
|
if (!methodInfo.IsGenericMethod)
|
|
return false;
|
|
|
|
Collection<GenericParameter> gparamRefs = methodRef.GenericParameters;
|
|
Type[] gparamInfos = methodInfo.GetGenericArguments();
|
|
if (gparamRefs.Count != gparamInfos.Length)
|
|
return false;
|
|
|
|
for (int i = 0; i < gparamRefs.Count; i++) {
|
|
if (!gparamRefs[i].Is(gparamInfos[i].GetTypeInfo()))
|
|
return false;
|
|
}
|
|
|
|
} else if (methodInfo.IsGenericMethod)
|
|
return false;
|
|
|
|
bool IsParamTypeRef(TypeReference paramTypeRef, TypeOrTypeInfo paramTypeInfo) {
|
|
if (paramTypeRef is GenericParameter paramGenParamTypeRef) {
|
|
if (paramGenParamTypeRef.Owner is MethodReference && methodRef is GenericInstanceMethod paramGenMethodRef &&
|
|
IsParamTypeRef(paramGenMethodRef.GenericArguments[paramGenParamTypeRef.Position], paramTypeInfo))
|
|
return true;
|
|
|
|
if (paramGenParamTypeRef.Owner is TypeReference paramGenParamTypeRefOwnerType && methodRef.DeclaringType is GenericInstanceType genTypeRefRef &&
|
|
paramGenParamTypeRefOwnerType.FullName == genTypeRefRef.ElementType.FullName && // This is to prevent List<Tuple<...>> checks from incorrectly checking Tuple's args in List.
|
|
IsParamTypeRef(genTypeRefRef.GenericArguments[paramGenParamTypeRef.Position], paramTypeInfo))
|
|
return true;
|
|
|
|
if (!paramTypeInfo.IsGenericParameter)
|
|
return false;
|
|
|
|
if (paramGenParamTypeRef.Position == paramTypeInfo.GenericParameterPosition)
|
|
return true;
|
|
}
|
|
|
|
if (paramTypeRef is GenericInstanceType paramGenTypeRef) {
|
|
if (!paramTypeInfo.IsGenericType)
|
|
return false;
|
|
|
|
Collection<TypeReference> gparamRefs = paramGenTypeRef.GenericArguments;
|
|
Type[] gparamInfos = paramTypeInfo.AsType().GetGenericArguments();
|
|
if (gparamRefs.Count != gparamInfos.Length)
|
|
return false;
|
|
|
|
for (int i = 0; i < gparamRefs.Count; i++) {
|
|
if (!IsParamTypeRef(gparamRefs[i], gparamInfos[i].GetTypeInfo()))
|
|
return false;
|
|
}
|
|
|
|
return IsParamTypeRef(paramGenTypeRef.ElementType, paramTypeInfo.GetGenericTypeDefinition().GetTypeInfo());
|
|
|
|
} else if (paramTypeRef.HasGenericParameters) {
|
|
if (!paramTypeInfo.IsGenericType)
|
|
return false;
|
|
|
|
Collection<GenericParameter> gparamRefs = paramTypeRef.GenericParameters;
|
|
Type[] gparamInfos = paramTypeInfo.AsType().GetGenericArguments();
|
|
if (gparamRefs.Count != gparamInfos.Length)
|
|
return false;
|
|
|
|
for (int i = 0; i < gparamRefs.Count; i++) {
|
|
if (!IsParamTypeRef(gparamRefs[i], gparamInfos[i].GetTypeInfo()))
|
|
return false;
|
|
}
|
|
|
|
} else if (paramTypeInfo.IsGenericType)
|
|
return false;
|
|
|
|
if (paramTypeRef is ArrayType paramArrayTypeRef) {
|
|
if (!paramTypeInfo.IsArray)
|
|
return false;
|
|
|
|
return paramArrayTypeRef.Dimensions.Count == paramTypeInfo.GetArrayRank() && IsParamTypeRef(paramArrayTypeRef.ElementType, paramTypeInfo.GetElementType().GetTypeInfo());
|
|
}
|
|
|
|
if (paramTypeRef is TypeSpecification paramTypeSpecRef)
|
|
// Note: There are TypeSpecifications which map to non-ElementType-y reflection Types.
|
|
return IsParamTypeRef(paramTypeSpecRef.ElementType, paramTypeInfo.HasElementType ? paramTypeInfo.GetElementType().GetTypeInfo() : paramTypeInfo);
|
|
|
|
if (paramTypeRef.DeclaringType != null && paramTypeInfo.DeclaringType != null)
|
|
return paramTypeRef.Name == paramTypeInfo.Name && IsParamTypeRef(paramTypeRef.DeclaringType, paramTypeInfo.DeclaringType.GetTypeInfo());
|
|
return paramTypeRef.FullName == paramTypeInfo.FullName;
|
|
}
|
|
|
|
for (int i = 0; i < paramRefs.Count; i++)
|
|
if (!IsParamTypeRef(paramRefs[i].ParameterType, paramInfos[i].ParameterType.GetTypeInfo()))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
if (mref is FieldReference && !(minfo is System.Reflection.FieldInfo))
|
|
return false;
|
|
|
|
if (mref is PropertyReference && !(minfo is System.Reflection.PropertyInfo))
|
|
return false;
|
|
|
|
if (mref is EventReference && !(minfo is System.Reflection.EventInfo))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
public static void UpdateOffsets(this MethodBody body, int instri, int delta) {
|
|
for (int offsi = body.Instructions.Count - 1; instri <= offsi; offsi--)
|
|
body.Instructions[offsi].Offset += delta;
|
|
}
|
|
|
|
public static int GetInt(this Instruction instr) {
|
|
OpCode op = instr.OpCode;
|
|
if (op == OpCodes.Ldc_I4_M1) return -1;
|
|
if (op == OpCodes.Ldc_I4_0) return 0;
|
|
if (op == OpCodes.Ldc_I4_1) return 1;
|
|
if (op == OpCodes.Ldc_I4_2) return 2;
|
|
if (op == OpCodes.Ldc_I4_3) return 3;
|
|
if (op == OpCodes.Ldc_I4_4) return 4;
|
|
if (op == OpCodes.Ldc_I4_5) return 5;
|
|
if (op == OpCodes.Ldc_I4_6) return 6;
|
|
if (op == OpCodes.Ldc_I4_7) return 7;
|
|
if (op == OpCodes.Ldc_I4_8) return 8;
|
|
if (op == OpCodes.Ldc_I4_S) return (sbyte) instr.Operand;
|
|
return (int) instr.Operand;
|
|
}
|
|
public static int? GetIntOrNull(this Instruction instr) {
|
|
OpCode op = instr.OpCode;
|
|
if (op == OpCodes.Ldc_I4_M1) return -1;
|
|
if (op == OpCodes.Ldc_I4_0) return 0;
|
|
if (op == OpCodes.Ldc_I4_1) return 1;
|
|
if (op == OpCodes.Ldc_I4_2) return 2;
|
|
if (op == OpCodes.Ldc_I4_3) return 3;
|
|
if (op == OpCodes.Ldc_I4_4) return 4;
|
|
if (op == OpCodes.Ldc_I4_5) return 5;
|
|
if (op == OpCodes.Ldc_I4_6) return 6;
|
|
if (op == OpCodes.Ldc_I4_7) return 7;
|
|
if (op == OpCodes.Ldc_I4_8) return 8;
|
|
if (op == OpCodes.Ldc_I4_S) return (sbyte) instr.Operand;
|
|
if (op == OpCodes.Ldc_I4) return (int) instr.Operand;
|
|
return null;
|
|
}
|
|
|
|
public static ParameterDefinition GetParam(this Instruction instr, MethodDefinition method) {
|
|
OpCode op = instr.OpCode;
|
|
int offs = method.HasThis ? -1 : 0;
|
|
if (op == OpCodes.Ldarg_0) return method.HasThis ? null : method.Parameters[offs + 0];
|
|
if (op == OpCodes.Ldarg_1) return method.Parameters[offs + 1];
|
|
if (op == OpCodes.Ldarg_2) return method.Parameters[offs + 2];
|
|
if (op == OpCodes.Ldarg_3) return method.Parameters[offs + 3];
|
|
if (op == OpCodes.Ldarg_S) return (ParameterDefinition) instr.Operand;
|
|
if (op == OpCodes.Ldarg) return (ParameterDefinition) instr.Operand;
|
|
return null;
|
|
}
|
|
|
|
public static VariableDefinition GetLocal(this Instruction instr, MethodDefinition method) {
|
|
OpCode op = instr.OpCode;
|
|
if (op == OpCodes.Ldloc_0) return method.Body.Variables[0];
|
|
if (op == OpCodes.Ldloc_1) return method.Body.Variables[1];
|
|
if (op == OpCodes.Ldloc_2) return method.Body.Variables[2];
|
|
if (op == OpCodes.Ldloc_3) return method.Body.Variables[3];
|
|
if (op == OpCodes.Ldloca_S) return (VariableDefinition) instr.Operand;
|
|
if (op == OpCodes.Ldloc) return (VariableDefinition) instr.Operand;
|
|
return null;
|
|
}
|
|
|
|
public static void AddRange<T>(this Collection<T> list, IEnumerable<T> other) {
|
|
foreach (T entry in other)
|
|
list.Add(entry);
|
|
}
|
|
public static void AddRange(this IDictionary dict, IDictionary other) {
|
|
foreach (DictionaryEntry entry in other)
|
|
dict.Add(entry.Key, entry.Value);
|
|
}
|
|
public static void AddRange<K, V>(this IDictionary<K, V> dict, IDictionary<K, V> other) {
|
|
foreach (KeyValuePair<K, V> entry in other)
|
|
dict.Add(entry.Key, entry.Value);
|
|
}
|
|
public static void AddRange<K, V>(this Dictionary<K, V> dict, Dictionary<K, V> other) {
|
|
foreach (KeyValuePair<K, V> entry in other)
|
|
dict.Add(entry.Key, entry.Value);
|
|
}
|
|
|
|
public static void InsertRange<T>(this Collection<T> list, int index, IEnumerable<T> other) {
|
|
foreach (T entry in other)
|
|
list.Insert(index++, entry);
|
|
}
|
|
|
|
public static void PushRange<T>(this Stack<T> stack, IEnumerable<T> other) {
|
|
foreach (T entry in other)
|
|
stack.Push(entry);
|
|
}
|
|
public static void PopRange<T>(this Stack<T> stack, int n) {
|
|
for (int i = 0; i < n; i++)
|
|
stack.Pop();
|
|
}
|
|
|
|
public static void EnqueueRange<T>(this Queue<T> queue, IEnumerable<T> other) {
|
|
foreach (T entry in other)
|
|
queue.Enqueue(entry);
|
|
}
|
|
public static void DequeueRange<T>(this Queue<T> queue, int n) {
|
|
for (int i = 0; i < n; i++)
|
|
queue.Dequeue();
|
|
}
|
|
|
|
public static T[] Clone<T>(this T[] array, int length) {
|
|
T[] clone = new T[length];
|
|
Array.Copy(array, clone, length);
|
|
return clone;
|
|
}
|
|
|
|
public static GenericParameter GetGenericParameter(this IGenericParameterProvider provider, GenericParameter orig) {
|
|
// Don't ask me, that's possible for T[,].Get in "Enter the Gungeon"...?!
|
|
if (provider is GenericParameter && ((GenericParameter) provider).Name == orig.Name)
|
|
return (GenericParameter) provider;
|
|
|
|
foreach (GenericParameter param in provider.GenericParameters)
|
|
if (param.Name == orig.Name)
|
|
return param;
|
|
|
|
int index = orig.Position;
|
|
if (provider is MethodReference && orig.DeclaringMethod != null) {
|
|
if (index < provider.GenericParameters.Count)
|
|
return provider.GenericParameters[index];
|
|
else
|
|
return new GenericParameter(orig.Name, provider).Update(index, GenericParameterType.Method);
|
|
}
|
|
|
|
if (provider is TypeReference && orig.DeclaringType != null)
|
|
if (index < provider.GenericParameters.Count)
|
|
return provider.GenericParameters[index];
|
|
else
|
|
return new GenericParameter(orig.Name, provider).Update(index, GenericParameterType.Type);
|
|
|
|
return
|
|
(provider as TypeSpecification)?.ElementType.GetGenericParameter(orig) ??
|
|
(provider as MemberReference)?.DeclaringType?.GetGenericParameter(orig);
|
|
}
|
|
|
|
public static IMetadataTokenProvider Relink(this IMetadataTokenProvider mtp, Relinker relinker, IGenericParameterProvider context) {
|
|
if (mtp is TypeReference) return ((TypeReference) mtp).Relink(relinker, context);
|
|
if (mtp is MethodReference) return ((MethodReference) mtp).Relink(relinker, context);
|
|
if (mtp is FieldReference) return ((FieldReference) mtp).Relink(relinker, context);
|
|
if (mtp is ParameterDefinition) return ((ParameterDefinition) mtp).Relink(relinker, context);
|
|
if (mtp is CallSite) return ((CallSite) mtp).Relink(relinker, context);
|
|
throw new InvalidOperationException($"MonoMod can't handle metadata token providers of the type {mtp.GetType()}");
|
|
}
|
|
|
|
public static TypeReference Relink(this TypeReference type, Relinker relinker, IGenericParameterProvider context) {
|
|
if (type == null)
|
|
return null;
|
|
|
|
if (type is TypeSpecification ts) {
|
|
TypeReference relinkedElem = ts.ElementType.Relink(relinker, context);
|
|
|
|
if (type.IsSentinel)
|
|
return new SentinelType(relinkedElem);
|
|
|
|
if (type.IsByReference)
|
|
return new ByReferenceType(relinkedElem);
|
|
|
|
if (type.IsPointer)
|
|
return new PointerType(relinkedElem);
|
|
|
|
if (type.IsPinned)
|
|
return new PinnedType(relinkedElem);
|
|
|
|
if (type.IsArray) {
|
|
ArrayType at = new ArrayType(relinkedElem, ((ArrayType) type).Rank);
|
|
for (int i = 0; i < at.Rank; i++)
|
|
// It's a struct.
|
|
at.Dimensions[i] = ((ArrayType) type).Dimensions[i];
|
|
return at;
|
|
}
|
|
|
|
if (type.IsRequiredModifier)
|
|
return new RequiredModifierType(((RequiredModifierType) type).ModifierType.Relink(relinker, context), relinkedElem);
|
|
|
|
if (type.IsOptionalModifier)
|
|
return new OptionalModifierType(((OptionalModifierType) type).ModifierType.Relink(relinker, context), relinkedElem);
|
|
|
|
if (type.IsGenericInstance) {
|
|
GenericInstanceType git = new GenericInstanceType(relinkedElem);
|
|
foreach (TypeReference genArg in ((GenericInstanceType) type).GenericArguments)
|
|
git.GenericArguments.Add(genArg?.Relink(relinker, context));
|
|
return git;
|
|
}
|
|
|
|
if (type.IsFunctionPointer) {
|
|
FunctionPointerType fp = (FunctionPointerType) type;
|
|
fp.ReturnType = fp.ReturnType.Relink(relinker, context);
|
|
for (int i = 0; i < fp.Parameters.Count; i++)
|
|
fp.Parameters[i].ParameterType = fp.Parameters[i].ParameterType.Relink(relinker, context);
|
|
return fp;
|
|
}
|
|
|
|
throw new NotSupportedException($"MonoMod can't handle TypeSpecification: {type.FullName} ({type.GetType()})");
|
|
}
|
|
|
|
if (type.IsGenericParameter) {
|
|
GenericParameter genParam = context.GetGenericParameter((GenericParameter) type);
|
|
if (genParam == null)
|
|
throw new RelinkTargetNotFoundException($"{RelinkTargetNotFoundException.DefaultMessage} {type.FullName} (context: {context})", type, context);
|
|
for (int i = 0; i < genParam.Constraints.Count; i++)
|
|
if (!genParam.Constraints[i].IsGenericInstance) // That is somehow possible and causes a stack overflow.
|
|
genParam.Constraints[i] = genParam.Constraints[i].Relink(relinker, context);
|
|
return genParam;
|
|
}
|
|
|
|
return (TypeReference) relinker(type, context);
|
|
}
|
|
|
|
public static MethodReference Relink(this MethodReference method, Relinker relinker, IGenericParameterProvider context) {
|
|
if (method.IsGenericInstance) {
|
|
GenericInstanceMethod methodg = (GenericInstanceMethod) method;
|
|
GenericInstanceMethod gim = new GenericInstanceMethod(methodg.ElementMethod.Relink(relinker, context));
|
|
foreach (TypeReference arg in methodg.GenericArguments)
|
|
// Generic arguments for the generic instance are often given by the next higher provider.
|
|
gim.GenericArguments.Add(arg.Relink(relinker, context));
|
|
|
|
return (MethodReference) relinker(gim, context);
|
|
}
|
|
|
|
MethodReference relink = new MethodReference(method.Name, method.ReturnType, method.DeclaringType.Relink(relinker, context));
|
|
|
|
relink.CallingConvention = method.CallingConvention;
|
|
relink.ExplicitThis = method.ExplicitThis;
|
|
relink.HasThis = method.HasThis;
|
|
|
|
foreach (GenericParameter param in method.GenericParameters) {
|
|
GenericParameter paramN = new GenericParameter(param.Name, param.Owner) {
|
|
Attributes = param.Attributes,
|
|
// MetadataToken = param.MetadataToken
|
|
}.Update(param);
|
|
|
|
relink.GenericParameters.Add(paramN);
|
|
|
|
foreach (TypeReference constraint in param.Constraints) {
|
|
paramN.Constraints.Add(constraint.Relink(relinker, relink));
|
|
}
|
|
}
|
|
|
|
relink.ReturnType = relink.ReturnType?.Relink(relinker, relink);
|
|
|
|
foreach (ParameterDefinition param in method.Parameters) {
|
|
param.ParameterType = param.ParameterType.Relink(relinker, method);
|
|
relink.Parameters.Add(param);
|
|
}
|
|
|
|
return (MethodReference) relinker(relink, context);
|
|
}
|
|
|
|
public static CallSite Relink(this CallSite method, Relinker relinker, IGenericParameterProvider context) {
|
|
CallSite relink = new CallSite(method.ReturnType);
|
|
|
|
relink.CallingConvention = method.CallingConvention;
|
|
relink.ExplicitThis = method.ExplicitThis;
|
|
relink.HasThis = method.HasThis;
|
|
|
|
relink.ReturnType = relink.ReturnType?.Relink(relinker, context);
|
|
|
|
foreach (ParameterDefinition param in method.Parameters) {
|
|
param.ParameterType = param.ParameterType.Relink(relinker, context);
|
|
relink.Parameters.Add(param);
|
|
}
|
|
|
|
return (CallSite) relinker(relink, context);
|
|
}
|
|
|
|
public static IMetadataTokenProvider Relink(this FieldReference field, Relinker relinker, IGenericParameterProvider context) {
|
|
TypeReference declaringType = field.DeclaringType.Relink(relinker, context);
|
|
return relinker(new FieldReference(field.Name, field.FieldType.Relink(relinker, declaringType), declaringType), context);
|
|
}
|
|
|
|
public static ParameterDefinition Relink(this ParameterDefinition param, Relinker relinker, IGenericParameterProvider context) {
|
|
param = (param.Method as MethodReference)?.Parameters[param.Index] ?? param;
|
|
ParameterDefinition newParam = new ParameterDefinition(param.Name, param.Attributes, param.ParameterType.Relink(relinker, context)) {
|
|
IsIn = param.IsIn,
|
|
IsLcid = param.IsLcid,
|
|
IsOptional = param.IsOptional,
|
|
IsOut = param.IsOut,
|
|
IsReturnValue = param.IsReturnValue,
|
|
MarshalInfo = param.MarshalInfo
|
|
};
|
|
if (param.HasConstant)
|
|
newParam.Constant = param.Constant;
|
|
return newParam;
|
|
}
|
|
|
|
public static ParameterDefinition Clone(this ParameterDefinition param) {
|
|
ParameterDefinition newParam = new ParameterDefinition(param.Name, param.Attributes, param.ParameterType) {
|
|
IsIn = param.IsIn,
|
|
IsLcid = param.IsLcid,
|
|
IsOptional = param.IsOptional,
|
|
IsOut = param.IsOut,
|
|
IsReturnValue = param.IsReturnValue,
|
|
MarshalInfo = param.MarshalInfo
|
|
};
|
|
if (param.HasConstant)
|
|
newParam.Constant = param.Constant;
|
|
foreach (CustomAttribute attrib in param.CustomAttributes)
|
|
newParam.CustomAttributes.Add(attrib.Clone());
|
|
return newParam;
|
|
}
|
|
|
|
public static CustomAttribute Relink(this CustomAttribute attrib, Relinker relinker, IGenericParameterProvider context) {
|
|
CustomAttribute newAttrib = new CustomAttribute(attrib.Constructor.Relink(relinker, context));
|
|
foreach (CustomAttributeArgument attribArg in attrib.ConstructorArguments)
|
|
newAttrib.ConstructorArguments.Add(new CustomAttributeArgument(attribArg.Type.Relink(relinker, context), attribArg.Value));
|
|
foreach (CustomAttributeNamedArgument attribArg in attrib.Fields)
|
|
newAttrib.Fields.Add(new CustomAttributeNamedArgument(attribArg.Name,
|
|
new CustomAttributeArgument(attribArg.Argument.Type.Relink(relinker, context), attribArg.Argument.Value))
|
|
);
|
|
foreach (CustomAttributeNamedArgument attribArg in attrib.Properties)
|
|
newAttrib.Properties.Add(new CustomAttributeNamedArgument(attribArg.Name,
|
|
new CustomAttributeArgument(attribArg.Argument.Type.Relink(relinker, context), attribArg.Argument.Value))
|
|
);
|
|
return newAttrib;
|
|
}
|
|
|
|
public static CustomAttribute Clone(this CustomAttribute attrib) {
|
|
CustomAttribute newAttrib = new CustomAttribute(attrib.Constructor);
|
|
foreach (CustomAttributeArgument attribArg in attrib.ConstructorArguments)
|
|
newAttrib.ConstructorArguments.Add(new CustomAttributeArgument(attribArg.Type, attribArg.Value));
|
|
foreach (CustomAttributeNamedArgument attribArg in attrib.Fields)
|
|
newAttrib.Fields.Add(new CustomAttributeNamedArgument(attribArg.Name,
|
|
new CustomAttributeArgument(attribArg.Argument.Type, attribArg.Argument.Value))
|
|
);
|
|
foreach (CustomAttributeNamedArgument attribArg in attrib.Properties)
|
|
newAttrib.Properties.Add(new CustomAttributeNamedArgument(attribArg.Name,
|
|
new CustomAttributeArgument(attribArg.Argument.Type, attribArg.Argument.Value))
|
|
);
|
|
return newAttrib;
|
|
}
|
|
|
|
public static GenericParameter Relink(this GenericParameter param, Relinker relinker, IGenericParameterProvider context) {
|
|
GenericParameter newParam = new GenericParameter(param.Name, param.Owner) {
|
|
Attributes = param.Attributes
|
|
}.Update(param);
|
|
foreach (TypeReference constraint in param.Constraints)
|
|
newParam.Constraints.Add(constraint.Relink(relinker, context));
|
|
return newParam;
|
|
}
|
|
|
|
public static GenericParameter Clone(this GenericParameter param) {
|
|
GenericParameter newParam = new GenericParameter(param.Name, param.Owner) {
|
|
Attributes = param.Attributes
|
|
}.Update(param);
|
|
foreach (TypeReference constraint in param.Constraints)
|
|
newParam.Constraints.Add(constraint);
|
|
return newParam;
|
|
}
|
|
|
|
public static MethodDefinition FindMethod(this TypeDefinition type, string findableID, bool simple = true) {
|
|
if (simple && !findableID.Contains(" ")) {
|
|
// Those shouldn't be reached, unless you're defining a relink map dynamically, which may conflict with itself.
|
|
// First simple pass: With type name (just "Namespace.Type::MethodName")
|
|
foreach (MethodDefinition method in type.Methods)
|
|
if (method.GetFindableID(simple: true) == findableID) return method;
|
|
// Second simple pass: Without type name (basically name only)
|
|
foreach (MethodDefinition method in type.Methods)
|
|
if (method.GetFindableID(withType: false, simple: true) == findableID) return method;
|
|
}
|
|
|
|
// First pass: With type name (f.e. global searches)
|
|
foreach (MethodDefinition method in type.Methods)
|
|
if (method.GetFindableID() == findableID) return method;
|
|
// Second pass: Without type name (f.e. LinkTo)
|
|
foreach (MethodDefinition method in type.Methods)
|
|
if (method.GetFindableID(withType: false) == findableID) return method;
|
|
|
|
return null;
|
|
}
|
|
public static MethodDefinition FindMethodDeep(this TypeDefinition type, string findableID, bool simple = true) {
|
|
return type.FindMethod(findableID, simple) ?? type.BaseType?.Resolve()?.FindMethodDeep(findableID, simple);
|
|
}
|
|
public static System.Reflection.MethodInfo FindMethod(this Type type, string findableID, bool simple = true) {
|
|
System.Reflection.MethodInfo[] methods = type.GetMethods(
|
|
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static |
|
|
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic
|
|
);
|
|
// First pass: With type name (f.e. global searches)
|
|
foreach (System.Reflection.MethodInfo method in methods)
|
|
if (method.GetFindableID() == findableID) return method;
|
|
// Second pass: Without type name (f.e. LinkTo)
|
|
foreach (System.Reflection.MethodInfo method in methods)
|
|
if (method.GetFindableID(withType: false) == findableID) return method;
|
|
|
|
if (!simple)
|
|
return null;
|
|
|
|
// Those shouldn't be reached, unless you're defining a relink map dynamically, which may conflict with itself.
|
|
// First simple pass: With type name (just "Namespace.Type::MethodName")
|
|
foreach (System.Reflection.MethodInfo method in methods)
|
|
if (method.GetFindableID(simple: true) == findableID) return method;
|
|
// Second simple pass: Without type name (basically name only)
|
|
foreach (System.Reflection.MethodInfo method in methods)
|
|
if (method.GetFindableID(withType: false, simple: true) == findableID) return method;
|
|
|
|
return null;
|
|
}
|
|
public static System.Reflection.MethodInfo FindMethodDeep(this Type type, string findableID, bool simple = true) {
|
|
return type.FindMethod(findableID, simple) ?? type.GetTypeInfo().BaseType?.FindMethodDeep(findableID, simple);
|
|
}
|
|
public static PropertyDefinition FindProperty(this TypeDefinition type, string name) {
|
|
foreach (PropertyDefinition prop in type.Properties)
|
|
if (prop.Name == name) return prop;
|
|
return null;
|
|
}
|
|
public static PropertyDefinition FindPropertyDeep(this TypeDefinition type, string name) {
|
|
return type.FindProperty(name) ?? type.BaseType?.Resolve()?.FindPropertyDeep(name);
|
|
}
|
|
public static FieldDefinition FindField(this TypeDefinition type, string name) {
|
|
foreach (FieldDefinition field in type.Fields)
|
|
if (field.Name == name) return field;
|
|
return null;
|
|
}
|
|
public static FieldDefinition FindFieldDeep(this TypeDefinition type, string name) {
|
|
return type.FindField(name) ?? type.BaseType?.Resolve()?.FindFieldDeep(name);
|
|
}
|
|
|
|
public static EventDefinition FindEvent(this TypeDefinition type, string name) {
|
|
foreach (EventDefinition eventDef in type.Events)
|
|
if (eventDef.Name == name) return eventDef;
|
|
return null;
|
|
}
|
|
public static EventDefinition FindEventDeep(this TypeDefinition type, string name) {
|
|
return type.FindEvent(name) ?? type.BaseType?.Resolve()?.FindEventDeep(name);
|
|
}
|
|
|
|
public static bool HasMethod(this TypeDefinition type, MethodDefinition method)
|
|
=> type.FindMethod(method.GetFindableID(withType: false)) != null;
|
|
public static bool HasProperty(this TypeDefinition type, PropertyDefinition prop)
|
|
=> type.FindProperty(prop.Name) != null;
|
|
public static bool HasField(this TypeDefinition type, FieldDefinition field)
|
|
=> type.FindField(field.Name) != null;
|
|
public static bool HasEvent(this TypeDefinition type, EventDefinition eventDef)
|
|
=> type.FindEvent(eventDef.Name) != null;
|
|
|
|
public static void SetPublic(this IMetadataTokenProvider mtp, bool p) {
|
|
if (mtp is TypeDefinition) ((TypeDefinition) mtp).SetPublic(p);
|
|
else if (mtp is FieldDefinition) ((FieldDefinition) mtp).SetPublic(p);
|
|
else if (mtp is MethodDefinition) ((MethodDefinition) mtp).SetPublic(p);
|
|
else if (mtp is PropertyDefinition) ((PropertyDefinition) mtp).SetPublic(p);
|
|
else if (mtp is EventDefinition) ((EventDefinition) mtp).SetPublic(p);
|
|
else throw new InvalidOperationException($"MonoMod can't set metadata token providers of the type {mtp.GetType()} public.");
|
|
}
|
|
public static void SetPublic(this FieldDefinition o, bool p) {
|
|
if (!o.IsDefinition || o.DeclaringType.Name == "<PrivateImplementationDetails>")
|
|
return;
|
|
o.IsPrivate = !p;
|
|
o.IsPublic = p;
|
|
if (p) o.DeclaringType.SetPublic(true);
|
|
}
|
|
public static void SetPublic(this MethodDefinition o, bool p) {
|
|
if (!o.IsDefinition || o.DeclaringType.Name == "<PrivateImplementationDetails>")
|
|
return;
|
|
o.IsPrivate = !p;
|
|
o.IsPublic = p;
|
|
if (p) o.DeclaringType.SetPublic(true);
|
|
}
|
|
public static void SetPublic(this PropertyDefinition o, bool p) {
|
|
if (!o.IsDefinition || o.DeclaringType.Name == "<PrivateImplementationDetails>")
|
|
return;
|
|
o.GetMethod?.SetPublic(p);
|
|
o.SetMethod?.SetPublic(p);
|
|
foreach (MethodDefinition method in o.OtherMethods)
|
|
method.SetPublic(p);
|
|
if (p) o.DeclaringType.SetPublic(true);
|
|
}
|
|
public static void SetPublic(this EventDefinition o, bool p) {
|
|
if (!o.IsDefinition || o.DeclaringType.Name == "<PrivateImplementationDetails>")
|
|
return;
|
|
o.AddMethod?.SetPublic(p);
|
|
o.RemoveMethod?.SetPublic(p);
|
|
o.InvokeMethod?.SetPublic(p);
|
|
foreach (MethodDefinition method in o.OtherMethods)
|
|
method.SetPublic(p);
|
|
if (p) o.DeclaringType.SetPublic(true);
|
|
}
|
|
public static void SetPublic(this TypeDefinition o, bool p) {
|
|
if (
|
|
!o.IsDefinition ||
|
|
o.Name == "<PrivateImplementationDetails>" ||
|
|
(o.DeclaringType != null && o.DeclaringType.Name == "<PrivateImplementationDetails>")
|
|
)
|
|
return;
|
|
if (o.DeclaringType == null) {
|
|
o.IsNotPublic = !p;
|
|
o.IsPublic = p;
|
|
} else {
|
|
o.IsNestedPrivate = !p;
|
|
o.IsNestedPublic = p;
|
|
if (p) SetPublic(o.DeclaringType, true);
|
|
}
|
|
}
|
|
|
|
public static Collection<T> Filtered<T>(this Collection<T> mtps, List<string> with, List<string> without) where T : IMetadataTokenProvider {
|
|
if (with.Count == 0 && without.Count == 0)
|
|
return mtps;
|
|
Collection<T> mtpsFiltered = new Collection<T>();
|
|
|
|
for (int i = 0; i < mtps.Count; i++) {
|
|
IMetadataTokenProvider mtp = mtps[i];
|
|
|
|
string name = null;
|
|
string nameAlt = null;
|
|
if (mtp is TypeReference) {
|
|
name = ((TypeReference) mtp).FullName;
|
|
} else if (mtp is MethodReference) {
|
|
name = ((MethodReference) mtp).GetFindableID(withType: true);
|
|
nameAlt = ((MethodReference) mtp).GetFindableID(simple: true);
|
|
} else if (mtp is FieldReference) {
|
|
name = ((FieldReference) mtp).Name;
|
|
}
|
|
|
|
if (without.Count != 0 && (without.Contains(name) || without.Contains(nameAlt)))
|
|
continue;
|
|
if (with.Count == 0 || with.Contains(name) || with.Contains(nameAlt)) {
|
|
mtpsFiltered.Add((T) mtp);
|
|
}
|
|
}
|
|
|
|
return mtpsFiltered;
|
|
}
|
|
|
|
public static IMetadataTokenProvider ImportReference(this ModuleDefinition mod, IMetadataTokenProvider mtp) {
|
|
if (mtp is TypeReference) return mod.ImportReference((TypeReference) mtp);
|
|
if (mtp is FieldReference) return mod.ImportReference((FieldReference) mtp);
|
|
if (mtp is MethodReference) return mod.ImportReference((MethodReference) mtp);
|
|
return mtp;
|
|
}
|
|
|
|
#if CECIL0_9
|
|
public static TypeReference ImportReference(this ModuleDefinition mod, TypeReference type)
|
|
=> mod.Import(type);
|
|
public static TypeReference ImportReference(this ModuleDefinition mod, Type type, IGenericParameterProvider context)
|
|
=> mod.Import(type, context);
|
|
public static FieldReference ImportReference(this ModuleDefinition mod, System.Reflection.FieldInfo field)
|
|
=> mod.Import(field);
|
|
public static FieldReference ImportReference(this ModuleDefinition mod, System.Reflection.FieldInfo field, IGenericParameterProvider context)
|
|
=> mod.Import(field, context);
|
|
public static MethodReference ImportReference(this ModuleDefinition mod, System.Reflection.MethodBase method)
|
|
=> mod.Import(method);
|
|
public static MethodReference ImportReference(this ModuleDefinition mod, System.Reflection.MethodBase method, IGenericParameterProvider context)
|
|
=> mod.Import(method, context);
|
|
public static TypeReference ImportReference(this ModuleDefinition mod, TypeReference type, IGenericParameterProvider context)
|
|
=> mod.Import(type, context);
|
|
public static TypeReference ImportReference(this ModuleDefinition mod, Type type)
|
|
=> mod.Import(type);
|
|
public static FieldReference ImportReference(this ModuleDefinition mod, FieldReference field)
|
|
=> mod.Import(field);
|
|
public static MethodReference ImportReference(this ModuleDefinition mod, MethodReference method)
|
|
=> mod.Import(method);
|
|
public static MethodReference ImportReference(this ModuleDefinition mod, MethodReference method, IGenericParameterProvider context)
|
|
=> mod.Import(method, context);
|
|
public static FieldReference ImportReference(this ModuleDefinition mod, FieldReference field, IGenericParameterProvider context)
|
|
=> mod.Import(field, context);
|
|
#endif
|
|
|
|
public static TypeDefinition SafeResolve(this TypeReference r) {
|
|
try {
|
|
return r.Resolve();
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static FieldDefinition SafeResolve(this FieldReference r) {
|
|
try {
|
|
return r.Resolve();
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static MethodDefinition SafeResolve(this MethodReference r) {
|
|
try {
|
|
return r.Resolve();
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static PropertyDefinition SafeResolve(this PropertyReference r) {
|
|
try {
|
|
return r.Resolve();
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static string GetPatchName(this MemberReference mr) {
|
|
return (mr as ICustomAttributeProvider)?.GetPatchName() ?? mr.Name;
|
|
}
|
|
public static string GetPatchFullName(this MemberReference mr) {
|
|
return (mr as ICustomAttributeProvider)?.GetPatchFullName(mr) ?? mr.FullName;
|
|
}
|
|
|
|
private static string GetPatchName(this ICustomAttributeProvider cap) {
|
|
string name;
|
|
|
|
CustomAttribute patchAttrib = cap.GetMMAttribute("Patch");
|
|
if (patchAttrib != null) {
|
|
name = ((string) patchAttrib.ConstructorArguments[0].Value).Inject(SharedData);
|
|
int dotIndex = name.LastIndexOf('.');
|
|
if (dotIndex != -1 && dotIndex != name.Length - 1) {
|
|
name = name.Substring(dotIndex + 1);
|
|
}
|
|
return name;
|
|
}
|
|
|
|
// Backwards-compatibility: Check for patch_
|
|
name = ((MemberReference) cap).Name;
|
|
return name.StartsWith("patch_") ? name.Substring(6) : name;
|
|
}
|
|
private static string GetPatchFullName(this ICustomAttributeProvider cap, MemberReference mr) {
|
|
if (cap is TypeReference type) {
|
|
CustomAttribute patchAttrib = cap.GetMMAttribute("Patch");
|
|
string name;
|
|
|
|
if (patchAttrib != null) {
|
|
name = ((string) patchAttrib.ConstructorArguments[0].Value).Inject(SharedData);
|
|
} else {
|
|
// Backwards-compatibility: Check for patch_
|
|
name = ((MemberReference) cap).Name;
|
|
name = name.StartsWith("patch_") ? name.Substring(6) : name;
|
|
}
|
|
|
|
if (name.StartsWith("global::"))
|
|
name = name.Substring(8); // Patch name is refering to a global type.
|
|
else if (name.Contains(".") || name.Contains("/")) { } // Patch name is already a full name.
|
|
else if (!string.IsNullOrEmpty(type.Namespace))
|
|
name = type.Namespace + "." + name;
|
|
else if (type.IsNested)
|
|
name = type.DeclaringType.GetPatchFullName() + "/" + name;
|
|
|
|
if (mr is TypeSpecification) {
|
|
// Collect TypeSpecifications and append formats back to front.
|
|
List<TypeSpecification> formats = new List<TypeSpecification>();
|
|
TypeSpecification ts = (TypeSpecification) mr;
|
|
do {
|
|
formats.Add(ts);
|
|
} while ((ts = (ts.ElementType as TypeSpecification)) != null);
|
|
|
|
StringBuilder builder = new StringBuilder(name.Length + formats.Count * 4);
|
|
builder.Append(name);
|
|
for (int formati = formats.Count - 1; formati > -1; --formati) {
|
|
ts = formats[formati];
|
|
|
|
if (ts.IsByReference)
|
|
builder.Append("&");
|
|
else if (ts.IsPointer)
|
|
builder.Append("*");
|
|
else if (ts.IsPinned) { } // FullName not overriden.
|
|
else if (ts.IsSentinel) { } // FullName not overriden.
|
|
else if (ts.IsArray) {
|
|
ArrayType array = (ArrayType) ts;
|
|
if (array.IsVector)
|
|
builder.Append("[]");
|
|
else {
|
|
builder.Append("[");
|
|
for (int i = 0; i < array.Dimensions.Count; i++) {
|
|
if (i > 0)
|
|
builder.Append(",");
|
|
builder.Append(array.Dimensions[i].ToString());
|
|
}
|
|
builder.Append("]");
|
|
}
|
|
} else if (ts.IsRequiredModifier)
|
|
builder.Append("modreq(").Append(((RequiredModifierType) ts).ModifierType).Append(")");
|
|
else if (ts.IsOptionalModifier)
|
|
builder.Append("modopt(").Append(((OptionalModifierType) ts).ModifierType).Append(")");
|
|
else if (ts.IsGenericInstance) {
|
|
GenericInstanceType gen = (GenericInstanceType) ts;
|
|
builder.Append("<");
|
|
for (int i = 0; i < gen.GenericArguments.Count; i++) {
|
|
if (i > 0)
|
|
builder.Append(",");
|
|
builder.Append(gen.GenericArguments[i].GetPatchFullName());
|
|
}
|
|
builder.Append(">");
|
|
} else if (ts.IsFunctionPointer) {
|
|
FunctionPointerType fpt = (FunctionPointerType) ts;
|
|
builder.Append(" ").Append(fpt.ReturnType.GetPatchFullName()).Append(" *(");
|
|
if (fpt.HasParameters)
|
|
for (int i = 0; i < fpt.Parameters.Count; i++) {
|
|
ParameterDefinition parameter = fpt.Parameters[i];
|
|
if (i > 0)
|
|
builder.Append(",");
|
|
|
|
if (parameter.ParameterType.IsSentinel)
|
|
builder.Append("...,");
|
|
|
|
builder.Append(parameter.ParameterType.FullName);
|
|
}
|
|
builder.Append(")");
|
|
} else
|
|
throw new NotSupportedException($"MonoMod can't handle TypeSpecification: {type.FullName} ({type.GetType()})");
|
|
}
|
|
|
|
name = builder.ToString();
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
if (cap is FieldReference field) {
|
|
return $"{field.FieldType.GetPatchFullName()} {field.DeclaringType.GetPatchFullName()}::{cap.GetPatchName()}";
|
|
}
|
|
|
|
if (cap is MethodReference)
|
|
throw new InvalidOperationException("GetPatchFullName not supported on MethodReferences - use GetFindableID instead");
|
|
|
|
throw new InvalidOperationException($"GetPatchFullName not supported on type {cap.GetType()}");
|
|
}
|
|
|
|
public static bool IsBaseMethodCall(this MethodBody body, MethodReference called) {
|
|
MethodDefinition caller = body.Method;
|
|
if (called == null)
|
|
return false;
|
|
TypeReference calledType = called.DeclaringType;
|
|
while (calledType is TypeSpecification)
|
|
calledType = ((TypeSpecification) calledType).ElementType;
|
|
string calledTypeName = calledType.GetPatchFullName();
|
|
|
|
bool callingBaseType = false;
|
|
try {
|
|
TypeDefinition baseType = caller.DeclaringType;
|
|
while ((baseType = baseType.BaseType?.SafeResolve()) != null)
|
|
if (baseType.GetPatchFullName() == calledTypeName) {
|
|
callingBaseType = true;
|
|
break;
|
|
}
|
|
} catch {
|
|
callingBaseType = caller.DeclaringType.GetPatchFullName() == calledTypeName;
|
|
}
|
|
if (!callingBaseType)
|
|
return false;
|
|
|
|
// return caller.IsMatchingSignature(called);
|
|
return true;
|
|
}
|
|
|
|
public static bool IsCallvirt(this MethodReference method) {
|
|
if (!method.HasThis)
|
|
return false;
|
|
if (method.DeclaringType.IsValueType)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
public static bool IsStruct(this TypeReference type) {
|
|
if (!type.IsValueType)
|
|
return false;
|
|
if (type.IsPrimitive)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
public static void RecalculateILOffsets(this MethodDefinition method) {
|
|
if (!method.HasBody)
|
|
return;
|
|
// Calculate updated offsets required for any further manual fixes.
|
|
int offs = 0;
|
|
for (int i = 0; i < method.Body.Instructions.Count; i++) {
|
|
Instruction instr = method.Body.Instructions[i];
|
|
instr.Offset = offs;
|
|
offs += instr.GetSize();
|
|
}
|
|
|
|
}
|
|
|
|
// Required for field -> call conversions where the original access was an address access.
|
|
public static void AppendGetAddr(this MethodBody body, Instruction instr, TypeReference type, IDictionary<TypeReference, VariableDefinition> localMap = null) {
|
|
if (localMap == null || !localMap.TryGetValue(type, out VariableDefinition local)) {
|
|
local = new VariableDefinition(type);
|
|
body.Variables.Add(local);
|
|
if (localMap != null)
|
|
localMap[type] = local;
|
|
}
|
|
|
|
ILProcessor il = body.GetILProcessor();
|
|
Instruction tmp = instr;
|
|
// TODO: Fast stloc / ldloca!
|
|
il.InsertAfter(tmp, tmp = il.Create(OpCodes.Stloc, local));
|
|
il.InsertAfter(tmp, tmp = il.Create(OpCodes.Ldloca, local));
|
|
}
|
|
|
|
public static void PrependGetValue(this MethodBody body, Instruction instr, TypeReference type) {
|
|
ILProcessor il = body.GetILProcessor();
|
|
il.InsertBefore(instr, il.Create(OpCodes.Ldobj, type));
|
|
}
|
|
|
|
public static void ConvertShortLongOps(this MethodDefinition method) {
|
|
if (!method.HasBody)
|
|
return;
|
|
|
|
// Convert short to long ops.
|
|
for (int i = 0; i < method.Body.Instructions.Count; i++) {
|
|
Instruction instr = method.Body.Instructions[i];
|
|
if (instr.Operand is Instruction) {
|
|
instr.OpCode = instr.OpCode.ShortToLongOp();
|
|
}
|
|
}
|
|
|
|
method.RecalculateILOffsets();
|
|
|
|
// Optimize long to short ops.
|
|
bool optimized;
|
|
do {
|
|
optimized = false;
|
|
for (int i = 0; i < method.Body.Instructions.Count; i++) {
|
|
Instruction instr = method.Body.Instructions[i];
|
|
// Change short <-> long operations as the method grows / shrinks.
|
|
if (instr.Operand is Instruction target) {
|
|
// Thanks to Chicken Bones for helping out with this!
|
|
int distance = target.Offset - (instr.Offset + instr.GetSize());
|
|
if (distance == (sbyte) distance) {
|
|
OpCode prev = instr.OpCode;
|
|
instr.OpCode = instr.OpCode.LongToShortOp();
|
|
optimized = prev != instr.OpCode;
|
|
}
|
|
}
|
|
}
|
|
} while (optimized);
|
|
}
|
|
|
|
private static readonly Type t_Code = typeof(Code);
|
|
private static readonly Type t_OpCodes = typeof(OpCodes);
|
|
|
|
private static readonly Dictionary<int, OpCode> _ShortToLongOp = new Dictionary<int, OpCode>();
|
|
public static OpCode ShortToLongOp(this OpCode op) {
|
|
string name = Enum.GetName(t_Code, op.Code);
|
|
if (!name.EndsWith("_S"))
|
|
return op;
|
|
if (_ShortToLongOp.TryGetValue((int) op.Code, out OpCode found))
|
|
return found;
|
|
return _ShortToLongOp[(int) op.Code] = (OpCode?) t_OpCodes.GetField(name.Substring(0, name.Length - 2))?.GetValue(null) ?? op;
|
|
}
|
|
|
|
private static readonly Dictionary<int, OpCode> _LongToShortOp = new Dictionary<int, OpCode>();
|
|
public static OpCode LongToShortOp(this OpCode op) {
|
|
string name = Enum.GetName(t_Code, op.Code);
|
|
if (name.EndsWith("_S"))
|
|
return op;
|
|
if (_LongToShortOp.TryGetValue((int) op.Code, out OpCode found))
|
|
return found;
|
|
return _LongToShortOp[(int) op.Code] = (OpCode?) t_OpCodes.GetField(name + "_S")?.GetValue(null) ?? op;
|
|
}
|
|
|
|
// IsMatchingSignature and related methods taken and adapted from the Mono.Linker:
|
|
// https://github.com/mono/linker/blob/e4dfcf006b0705aba6b204aab2d603b781c5fc44/linker/Mono.Linker.Steps/TypeMapStep.cs
|
|
|
|
public static bool IsMatchingSignature(this MethodDefinition method, MethodReference candidate) {
|
|
if (method.Parameters.Count != candidate.Parameters.Count)
|
|
return false;
|
|
|
|
if (method.Name != candidate.Name)
|
|
return false;
|
|
|
|
if (!method.ReturnType._InflateGenericType(method).IsMatchingSignature(
|
|
candidate.ReturnType._InflateGenericType(candidate)
|
|
))
|
|
return false;
|
|
|
|
if (method.GenericParameters.Count != candidate.GenericParameters.Count)
|
|
return false;
|
|
|
|
if (method.HasParameters) {
|
|
for (int i = 0; i < method.Parameters.Count; i++)
|
|
if (!method.Parameters[i].ParameterType._InflateGenericType(method).IsMatchingSignature(
|
|
candidate.Parameters[i].ParameterType._InflateGenericType(candidate)
|
|
))
|
|
return false;
|
|
}
|
|
|
|
if (!candidate.SafeResolve()?.IsVirtual ?? false)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool IsMatchingSignature(this IModifierType a, IModifierType b) {
|
|
if (!a.ModifierType.IsMatchingSignature(b.ModifierType))
|
|
return false;
|
|
|
|
return a.ElementType.IsMatchingSignature(b.ElementType);
|
|
}
|
|
|
|
public static bool IsMatchingSignature(this TypeSpecification a, TypeSpecification b) {
|
|
if (a.GetType() != b.GetType())
|
|
return false;
|
|
|
|
if (a is GenericInstanceType gita)
|
|
return gita.IsMatchingSignature((GenericInstanceType) b);
|
|
|
|
if (a is IModifierType mta)
|
|
return mta.IsMatchingSignature((IModifierType) b);
|
|
|
|
return IsMatchingSignature(a.ElementType, b.ElementType);
|
|
}
|
|
|
|
public static bool IsMatchingSignature(this GenericInstanceType a, GenericInstanceType b) {
|
|
if (!a.ElementType.IsMatchingSignature(b.ElementType))
|
|
return false;
|
|
|
|
if (a.HasGenericArguments != b.HasGenericArguments)
|
|
return false;
|
|
|
|
if (!a.HasGenericArguments)
|
|
return true;
|
|
|
|
if (a.GenericArguments.Count != b.GenericArguments.Count)
|
|
return false;
|
|
|
|
for (int i = 0; i < a.GenericArguments.Count; i++) {
|
|
if (!a.GenericArguments[i].IsMatchingSignature(b.GenericArguments[i]))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool IsMatchingSignature(this GenericParameter a, GenericParameter b) {
|
|
if (a.Position != b.Position)
|
|
return false;
|
|
|
|
if (a.Type != b.Type)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool IsMatchingSignature(this TypeReference a, TypeReference b) {
|
|
if (a is TypeSpecification || b is TypeSpecification)
|
|
return
|
|
(a is TypeSpecification && b is TypeSpecification) &&
|
|
((TypeSpecification) a).IsMatchingSignature((TypeSpecification) b);
|
|
|
|
if (a is GenericParameter && b is GenericParameter)
|
|
return ((GenericParameter) a).IsMatchingSignature((GenericParameter) b);
|
|
|
|
return a.FullName == b.FullName;
|
|
}
|
|
|
|
private static TypeReference _InflateGenericType(this TypeReference type, MethodReference method) {
|
|
if (!(method.DeclaringType is GenericInstanceType))
|
|
return type;
|
|
return _InflateGenericType(method.DeclaringType as GenericInstanceType, type);
|
|
}
|
|
|
|
private static TypeReference _InflateGenericType(GenericInstanceType genericInstanceProvider, TypeReference typeToInflate) {
|
|
if (typeToInflate is ArrayType arrayType) {
|
|
TypeReference inflatedElementType = _InflateGenericType(genericInstanceProvider, arrayType.ElementType);
|
|
|
|
if (inflatedElementType != arrayType.ElementType) {
|
|
ArrayType at = new ArrayType(inflatedElementType, arrayType.Rank);
|
|
for (int i = 0; i < arrayType.Rank; i++)
|
|
// It's a struct.
|
|
at.Dimensions[i] = arrayType.Dimensions[i];
|
|
return at;
|
|
}
|
|
|
|
return arrayType;
|
|
}
|
|
|
|
if (typeToInflate is GenericInstanceType genericInst) {
|
|
GenericInstanceType result = new GenericInstanceType(genericInst.ElementType);
|
|
|
|
for (int i = 0; i < genericInst.GenericArguments.Count; ++i)
|
|
result.GenericArguments.Add(_InflateGenericType(genericInstanceProvider, genericInst.GenericArguments[i]));
|
|
|
|
return result;
|
|
}
|
|
|
|
if (typeToInflate is GenericParameter genericParameter) {
|
|
if (genericParameter.Owner is MethodReference)
|
|
return genericParameter;
|
|
|
|
TypeDefinition elementType = genericInstanceProvider.ElementType.Resolve();
|
|
GenericParameter parameter = elementType.GetGenericParameter(genericParameter);
|
|
return genericInstanceProvider.GenericArguments[parameter.Position];
|
|
}
|
|
|
|
if (typeToInflate is FunctionPointerType functionPointerType) {
|
|
FunctionPointerType result = new FunctionPointerType();
|
|
result.ReturnType = _InflateGenericType(genericInstanceProvider, functionPointerType.ReturnType);
|
|
|
|
for (int i = 0; i < functionPointerType.Parameters.Count; i++)
|
|
result.Parameters.Add(new ParameterDefinition(_InflateGenericType(genericInstanceProvider, functionPointerType.Parameters[i].ParameterType)));
|
|
|
|
return result;
|
|
}
|
|
|
|
if (typeToInflate is IModifierType modifierType) {
|
|
TypeReference modifier = _InflateGenericType(genericInstanceProvider, modifierType.ModifierType);
|
|
TypeReference elementType = _InflateGenericType(genericInstanceProvider, modifierType.ElementType);
|
|
|
|
if (modifierType is OptionalModifierType)
|
|
return new OptionalModifierType(modifier, elementType);
|
|
|
|
return new RequiredModifierType(modifier, elementType);
|
|
}
|
|
|
|
if (typeToInflate is PinnedType pinnedType) {
|
|
TypeReference elementType = _InflateGenericType(genericInstanceProvider, pinnedType.ElementType);
|
|
|
|
if (elementType != pinnedType.ElementType)
|
|
return new PinnedType(elementType);
|
|
|
|
return pinnedType;
|
|
}
|
|
|
|
if (typeToInflate is PointerType pointerType) {
|
|
TypeReference elementType = _InflateGenericType(genericInstanceProvider, pointerType.ElementType);
|
|
|
|
if (elementType != pointerType.ElementType)
|
|
return new PointerType(elementType);
|
|
|
|
return pointerType;
|
|
}
|
|
|
|
if (typeToInflate is ByReferenceType byReferenceType) {
|
|
TypeReference elementType = _InflateGenericType(genericInstanceProvider, byReferenceType.ElementType);
|
|
|
|
if (elementType != byReferenceType.ElementType)
|
|
return new ByReferenceType(elementType);
|
|
|
|
return byReferenceType;
|
|
}
|
|
|
|
if (typeToInflate is SentinelType sentinelType) {
|
|
TypeReference elementType = _InflateGenericType(genericInstanceProvider, sentinelType.ElementType);
|
|
|
|
if (elementType != sentinelType.ElementType)
|
|
return new SentinelType(elementType);
|
|
|
|
return sentinelType;
|
|
}
|
|
|
|
return typeToInflate;
|
|
}
|
|
|
|
}
|
|
}
|