using System;
using System.Linq;
using System.Reflection;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace StardewModdingAPI.AssemblyRewriters.Framework
{
/// 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)
{
if (instruction.OpCode != OpCodes.Call && instruction.OpCode != OpCodes.Callvirt)
return false; // not a method reference
return this.ShouldRewrite(instruction, (MethodReference)instruction.Operand, 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 a method reference should be rewritten.
/// The IL instruction.
/// The method reference.
/// Whether the mod was compiled on a different platform.
protected abstract bool ShouldRewrite(Instruction instruction, 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 (!RewriteHelper.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));
}
}
}