using System;
using System.Linq;
using System.Reflection;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace StardewModdingAPI.AssemblyRewriters.Rewriters
{
/// Base class for a method rewriter.
public abstract class BaseMethodRewriter : IInstructionRewriter
{
/*********
** Public methods
*********/
/// Get whether a CIL instruction should be rewritten.
/// The IL instruction.
/// Whether the mod was compiled on a different platform.
public bool ShouldRewrite(Instruction instruction, bool platformChanged)
{
// ignore non-method-call instructions
if (instruction.OpCode != OpCodes.Call && instruction.OpCode != OpCodes.Callvirt)
return false;
// check reference
MethodReference methodRef = (MethodReference)instruction.Operand;
return this.ShouldRewrite(methodRef, platformChanged);
}
/// Rewrite a CIL instruction for compatibility.
/// The module being rewritten.
/// The CIL rewriter.
/// The instruction to rewrite.
/// Metadata for mapping assemblies to the current platform.
public void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap)
{
MethodReference methodRef = (MethodReference)instruction.Operand;
this.Rewrite(module, cil, instruction, methodRef, assemblyMap);
}
/*********
** Protected methods
*********/
/// Get whether the given method reference can be rewritten.
/// The method reference.
/// Whether the mod was compiled on a different platform.
protected abstract bool ShouldRewrite(MethodReference methodRef, bool platformChanged);
/// Rewrite a method for compatibility.
/// The module being rewritten.
/// The CIL rewriter.
/// The instruction which calls the method.
/// The method reference invoked by the .
/// Metadata for mapping assemblies to the current platform.
protected abstract void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, MethodReference methodRef, PlatformAssemblyMap assemblyMap);
/// Get whether a method definition matches the signature expected by a method reference.
/// The method definition.
/// The method reference.
protected bool HasMatchingSignature(MethodInfo definition, MethodReference reference)
{
// same name
if (definition.Name != reference.Name)
return false;
// same arguments
ParameterInfo[] definitionParameters = definition.GetParameters();
ParameterDefinition[] referenceParameters = reference.Parameters.ToArray();
if (referenceParameters.Length != definitionParameters.Length)
return false;
for (int i = 0; i < referenceParameters.Length; i++)
{
if (!this.IsMatchingType(definitionParameters[i].ParameterType, referenceParameters[i].ParameterType))
return false;
}
return true;
}
/// Get whether a type has a method whose signature matches the one expected by a method reference.
/// The type to check.
/// The method reference.
protected bool HasMatchingSignature(Type type, MethodReference reference)
{
return type
.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public)
.Any(method => this.HasMatchingSignature(method, reference));
}
/// Get whether a type matches a type reference.
/// The defined type.
/// The type reference.
private bool IsMatchingType(Type type, TypeReference reference)
{
// same namespace & name
if (type.Namespace != reference.Namespace || type.Name != reference.Name)
return false;
// same generic parameters
if (type.IsGenericType)
{
if (!reference.IsGenericInstance)
return false;
Type[] defGenerics = type.GetGenericArguments();
TypeReference[] refGenerics = ((GenericInstanceType)reference).GenericArguments.ToArray();
if (defGenerics.Length != refGenerics.Length)
return false;
for (int i = 0; i < defGenerics.Length; i++)
{
if (!this.IsMatchingType(defGenerics[i], refGenerics[i]))
return false;
}
}
return true;
}
}
}