using System; using Mono.Cecil; using Mono.Cecil.Cil; using StardewModdingAPI.Framework.ModLoading.Finders; namespace StardewModdingAPI.Framework.ModLoading.Rewriters { /// Rewrites field references into property references. internal class FieldToPropertyRewriter : FieldFinder { /********* ** Properties *********/ /// The type whose field to which references should be rewritten. private readonly Type Type; /// The property name. private readonly string PropertyName; /********* ** Public methods *********/ /// Construct an instance. /// The type whose field to which references should be rewritten. /// The field name to rewrite. /// The property name (if different). public FieldToPropertyRewriter(Type type, string fieldName, string propertyName) : base(type.FullName, fieldName, InstructionHandleResult.None) { this.Type = type; this.PropertyName = propertyName; } /// Construct an instance. /// The type whose field to which references should be rewritten. /// The field name to rewrite. public FieldToPropertyRewriter(Type type, string fieldName) : this(type, fieldName, fieldName) { } /// Perform the predefined logic for an instruction if applicable. /// The assembly module containing the instruction. /// The CIL processor. /// The instruction to handle. /// Metadata for mapping assemblies to the current platform. /// Whether the mod was compiled on a different platform. public override InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { if (!this.IsMatch(instruction)) return InstructionHandleResult.None; string methodPrefix = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld ? "get" : "set"; MethodReference propertyRef = module.Import(this.Type.GetMethod($"{methodPrefix}_{this.PropertyName}")); cil.Replace(instruction, cil.Create(OpCodes.Call, propertyRef)); return InstructionHandleResult.Rewritten; } } }