using System; using Mono.Cecil; using Mono.Cecil.Cil; using StardewModdingAPI.Framework.ModLoading.Framework; namespace StardewModdingAPI.Framework.ModLoading.Rewriters { /// Rewrites field references into property references. internal class FieldToPropertyRewriter : BaseInstructionHandler { /********* ** Fields *********/ /// The type containing the field to which references should be rewritten. private readonly Type Type; /// The field name to which references should be rewritten. private readonly string FromFieldName; /// The new property name. private readonly string ToPropertyName; /********* ** 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(defaultPhrase: $"{type.FullName}.{fieldName} field") { this.Type = type; this.FromFieldName = fieldName; this.ToPropertyName = 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) { } /// Rewrite a CIL instruction reference if needed. /// The assembly module containing the instruction. /// The CIL processor. /// The CIL instruction to handle. /// Replaces the CIL instruction with a new one. /// Returns whether the instruction was changed. public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, Action replaceWith) { // get field ref FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction); if (!RewriteHelper.IsFieldReferenceTo(fieldRef, this.Type.FullName, this.FromFieldName)) return false; // replace with property string methodPrefix = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld ? "get" : "set"; MethodReference propertyRef = module.ImportReference(this.Type.GetMethod($"{methodPrefix}_{this.ToPropertyName}")); replaceWith(cil.Create(OpCodes.Call, propertyRef)); return this.MarkRewritten(); } } }