summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs')
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs65
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();
}
}
}