diff options
Diffstat (limited to 'src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs')
-rw-r--r-- | src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs | 65 |
1 files changed, 51 insertions, 14 deletions
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs index 5a088ed8..ca04205c 100644 --- a/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs +++ b/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs @@ -6,7 +6,7 @@ using StardewModdingAPI.Framework.ModLoading.Framework; namespace StardewModdingAPI.Framework.ModLoading.Rewriters { - /// <summary>Automatically fix references to fields that have been replaced by a property.</summary> + /// <summary>Automatically fix references to fields that have been replaced by a property or const field.</summary> internal class HeuristicFieldRewriter : BaseInstructionHandler { /********* @@ -36,14 +36,40 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters return false; // skip if not broken - if (fieldRef.Resolve() != null) + FieldDefinition fieldDefinition = fieldRef.Resolve(); + if (fieldDefinition != null && !fieldDefinition.HasConstant) return false; + // rewrite if possible + TypeDefinition declaringType = fieldRef.DeclaringType.Resolve(); + bool isRead = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld; + return + this.TryRewriteToProperty(module, instruction, fieldRef, declaringType, isRead) + || this.TryRewriteToConstField(instruction, fieldDefinition); + } + + + /********* + ** Private methods + *********/ + /// <summary>Whether references to the given type should be validated.</summary> + /// <param name="type">The type reference.</param> + private bool ShouldValidate(TypeReference type) + { + return type != null && this.RewriteReferencesToAssemblies.Contains(type.Scope.Name); + } + + /// <summary>Try rewriting the field into a matching property.</summary> + /// <param name="module">The assembly module containing the instruction.</param> + /// <param name="instruction">The CIL instruction to rewrite.</param> + /// <param name="fieldRef">The field reference.</param> + /// <param name="declaringType">The type on which the field was defined.</param> + /// <param name="isRead">Whether the field is being read; else it's being written to.</param> + private bool TryRewriteToProperty(ModuleDefinition module, Instruction instruction, FieldReference fieldRef, TypeDefinition declaringType, bool isRead) + { // get equivalent property - PropertyDefinition property = fieldRef.DeclaringType.Resolve().Properties.FirstOrDefault(p => p.Name == fieldRef.Name); - MethodDefinition method = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld - ? property?.GetMethod - : property?.SetMethod; + PropertyDefinition property = declaringType.Properties.FirstOrDefault(p => p.Name == fieldRef.Name); + MethodDefinition method = isRead ? property?.GetMethod : property?.SetMethod; if (method == null) return false; @@ -55,15 +81,26 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters return this.MarkRewritten(); } - - /********* - ** Private methods - *********/ - /// <summary>Whether references to the given type should be validated.</summary> - /// <param name="type">The type reference.</param> - private bool ShouldValidate(TypeReference type) + /// <summary>Try rewriting the field into a matching const field.</summary> + /// <param name="instruction">The CIL instruction to rewrite.</param> + /// <param name="field">The field definition.</param> + private bool TryRewriteToConstField(Instruction instruction, FieldDefinition field) { - return type != null && this.RewriteReferencesToAssemblies.Contains(type.Scope.Name); + // must have been a static field read, and the new field must be const + if (instruction.OpCode != OpCodes.Ldsfld || field?.HasConstant != true) + return false; + + // get opcode for value type + Instruction loadInstruction = RewriteHelper.GetLoadValueInstruction(field.Constant); + if (loadInstruction == null) + return false; + + // rewrite to constant + instruction.OpCode = loadInstruction.OpCode; + instruction.Operand = loadInstruction.Operand; + + this.Phrases.Add($"{field.DeclaringType.Name}.{field.Name} (field => const)"); + return this.MarkRewritten(); } } } |