From 4d48bdfe7c3806ec1995cd499ca9382ace2d8a53 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 25 Mar 2017 13:50:01 -0400 Subject: drop 'generic' prefix for rewriters since they're all generic now --- .../Finders/EventFinder.cs | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs new file mode 100644 index 00000000..359ca63e --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs @@ -0,0 +1,54 @@ +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; + +namespace StardewModdingAPI.AssemblyRewriters.Finders +{ + /// Finds CIL instructions that reference a given event. + public sealed class EventFinder : BaseMethodFinder + { + /********* + ** Properties + *********/ + /// The full type name for which to find references. + private readonly string FullTypeName; + + /// The event name for which to find references. + private readonly string EventName; + + + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public override string NounPhrase { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The full type name for which to find references. + /// The event name for which to find references. + public EventFinder(string fullTypeName, string eventName) + { + this.FullTypeName = fullTypeName; + this.EventName = eventName; + this.NounPhrase = $"obsolete {fullTypeName}.{eventName} event"; + } + + + /********* + ** 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 override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) + { + return methodRef.DeclaringType.FullName == this.FullTypeName + && (methodRef.Name == "add_" + this.EventName || methodRef.Name == "remove_" + this.EventName); + } + } +} -- cgit From 7b641d816466fe7d9229374c175f59ee32b8dc5c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 25 Mar 2017 15:17:26 -0400 Subject: simplify CIL rewriter hierarchy --- .../Finders/EventFinder.cs | 15 ++-- .../Finders/FieldFinder.cs | 23 +++--- .../Finders/MethodFinder.cs | 15 ++-- .../Framework/BaseFieldFinder.cs | 46 ----------- .../Framework/BaseFieldRewriter.cs | 35 -------- .../Framework/BaseMethodFinder.cs | 74 ----------------- .../Framework/BaseMethodRewriter.cs | 35 -------- .../Framework/RewriteHelper.cs | 41 ---------- .../RewriteHelper.cs | 94 ++++++++++++++++++++++ .../Rewriters/FieldReplaceRewriter.cs | 37 ++------- .../Rewriters/FieldToPropertyRewriter.cs | 31 ++----- .../Rewriters/MethodParentRewriter.cs | 24 +++--- .../StardewModdingAPI.AssemblyRewriters.csproj | 6 +- src/StardewModdingAPI/Constants.cs | 2 +- 14 files changed, 146 insertions(+), 332 deletions(-) delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldFinder.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldRewriter.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodFinder.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/RewriteHelper.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/RewriteHelper.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs index 359ca63e..9d0184c6 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs @@ -1,11 +1,10 @@ using Mono.Cecil; using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; namespace StardewModdingAPI.AssemblyRewriters.Finders { /// Finds CIL instructions that reference a given event. - public sealed class EventFinder : BaseMethodFinder + public sealed class EventFinder : IInstructionFinder { /********* ** Properties @@ -21,7 +20,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders ** Accessors *********/ /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } + public string NounPhrase { get; } /********* @@ -41,13 +40,15 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /********* ** Protected methods *********/ - /// Get whether a method reference should be rewritten. + /// Get whether a CIL instruction matches. /// The IL instruction. - /// The method reference. /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) + public bool IsMatch(Instruction instruction, bool platformChanged) { - return methodRef.DeclaringType.FullName == this.FullTypeName + MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); + return + methodRef != null + && methodRef.DeclaringType.FullName == this.FullTypeName && (methodRef.Name == "add_" + this.EventName || methodRef.Name == "remove_" + this.EventName); } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs index 516641f2..068119b8 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs @@ -1,11 +1,10 @@ using Mono.Cecil; using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; namespace StardewModdingAPI.AssemblyRewriters.Finders { /// Finds CIL instructions that reference a given field. - public sealed class FieldFinder : BaseFieldFinder + public class FieldFinder : IInstructionFinder { /********* ** Properties @@ -16,15 +15,12 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /// The field name for which to find references. private readonly string FieldName; - /// Whether the field to match is static. - private readonly bool IsStatic; - /********* ** Accessors *********/ /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } + public string NounPhrase { get; } /********* @@ -33,27 +29,26 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /// Construct an instance. /// The full type name for which to find references. /// The field name for which to find references. - /// Whether the field to match is static. - public FieldFinder(string fullTypeName, string fieldName, bool isStatic) + /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). + public FieldFinder(string fullTypeName, string fieldName, string nounPhrase = null) { this.FullTypeName = fullTypeName; this.FieldName = fieldName; - this.IsStatic = isStatic; - this.NounPhrase = $"obsolete {fullTypeName}.{fieldName} field"; + this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{fieldName} field"; } /********* ** Protected methods *********/ - /// Get whether a field reference should be rewritten. + /// Get whether a CIL instruction matches. /// The IL instruction. - /// The field reference. /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) + public bool IsMatch(Instruction instruction, bool platformChanged) { + FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction); return - this.IsStaticField(instruction) == this.IsStatic + fieldRef != null && fieldRef.DeclaringType.FullName == this.FullTypeName && fieldRef.Name == this.FieldName; } diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs index 6c210d68..bea549ee 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs @@ -1,11 +1,10 @@ using Mono.Cecil; using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; namespace StardewModdingAPI.AssemblyRewriters.Finders { /// Finds CIL instructions that reference a given method. - public sealed class MethodFinder : BaseMethodFinder + public class MethodFinder : IInstructionFinder { /********* ** Properties @@ -21,7 +20,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders ** Accessors *********/ /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } + public string NounPhrase { get; } /********* @@ -41,13 +40,15 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /********* ** Protected methods *********/ - /// Get whether a method reference should be rewritten. + /// Get whether a CIL instruction matches. /// The IL instruction. - /// The method reference. /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) + public bool IsMatch(Instruction instruction, bool platformChanged) { - return methodRef.DeclaringType.FullName == this.FullTypeName + MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); + return + methodRef != null + && methodRef.DeclaringType.FullName == this.FullTypeName && methodRef.Name == this.MethodName; } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldFinder.cs deleted file mode 100644 index ac2facec..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldFinder.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace StardewModdingAPI.AssemblyRewriters.Framework -{ - /// Base class for a field finder. - public abstract class BaseFieldFinder : IInstructionFinder - { - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public abstract string NounPhrase { get; } - - - /********* - ** Public methods - *********/ - /// Get whether a CIL instruction matches. - /// The IL instruction. - /// Whether the mod was compiled on a different platform. - public bool IsMatch(Instruction instruction, bool platformChanged) - { - if (instruction.OpCode != OpCodes.Ldfld && instruction.OpCode != OpCodes.Ldsfld && instruction.OpCode != OpCodes.Stfld && instruction.OpCode != OpCodes.Stsfld) - return false; // not a field reference - return this.IsMatch(instruction, (FieldReference)instruction.Operand, platformChanged); - } - - - /********* - ** Protected methods - *********/ - /// Get whether a field reference should be rewritten. - /// The IL instruction. - /// The field reference. - /// Whether the mod was compiled on a different platform. - protected abstract bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged); - - /// Whether an instruction is a static field reference. - /// The IL instruction. - protected bool IsStaticField(Instruction instruction) - { - return instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stsfld; - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldRewriter.cs deleted file mode 100644 index b2c25587..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldRewriter.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace StardewModdingAPI.AssemblyRewriters.Framework -{ - /// Base class for a field rewriter. - public abstract class BaseFieldRewriter : BaseFieldFinder, IInstructionRewriter - { - /********* - ** Public methods - *********/ - /// 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) - { - FieldReference fieldRef = (FieldReference)instruction.Operand; - this.Rewrite(module, cil, instruction, fieldRef, assemblyMap); - } - - - /********* - ** Protected methods - *********/ - /// Rewrite a method for compatibility. - /// The module being rewritten. - /// The CIL rewriter. - /// The instruction which references the field. - /// The field reference invoked by the . - /// Metadata for mapping assemblies to the current platform. - protected abstract void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap); - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodFinder.cs deleted file mode 100644 index bb71a9d7..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodFinder.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace StardewModdingAPI.AssemblyRewriters.Framework -{ - /// Base class for a method finder. - public abstract class BaseMethodFinder : IInstructionFinder - { - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public abstract string NounPhrase { get; } - - - /********* - ** Public methods - *********/ - /// Get whether a CIL instruction matches. - /// The IL instruction. - /// Whether the mod was compiled on a different platform. - public bool IsMatch(Instruction instruction, bool platformChanged) - { - if (instruction.OpCode != OpCodes.Call && instruction.OpCode != OpCodes.Callvirt) - return false; // not a method reference - return this.IsMatch(instruction, (MethodReference)instruction.Operand, platformChanged); - } - - - /********* - ** 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 IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged); - - /// 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)); - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs deleted file mode 100644 index 6af1a0e1..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace StardewModdingAPI.AssemblyRewriters.Framework -{ - /// Base class for a method rewriter. - public abstract class BaseMethodRewriter : BaseMethodFinder, IInstructionRewriter - { - /********* - ** Public methods - *********/ - /// 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 - *********/ - /// 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); - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/RewriteHelper.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/RewriteHelper.cs deleted file mode 100644 index 0307053f..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Framework/RewriteHelper.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using Mono.Cecil; - -namespace StardewModdingAPI.AssemblyRewriters.Framework -{ - /// Provides helper methods for field rewriters. - internal static class RewriteHelper - { - /********* - ** Public methods - *********/ - /// Get whether a type matches a type reference. - /// The defined type. - /// The type reference. - public static bool IsMatchingType(Type type, TypeReference reference) - { - // same namespace & name - if (type.Namespace != reference.Namespace || type.Name != reference.Name) - return false; - - // same generic parameters - if (type.IsGenericType) - { - if (!reference.IsGenericInstance) - return false; - - Type[] defGenerics = type.GetGenericArguments(); - TypeReference[] refGenerics = ((GenericInstanceType)reference).GenericArguments.ToArray(); - if (defGenerics.Length != refGenerics.Length) - return false; - for (int i = 0; i < defGenerics.Length; i++) - { - if (!RewriteHelper.IsMatchingType(defGenerics[i], refGenerics[i])) - return false; - } - } - - return true; - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/RewriteHelper.cs b/src/StardewModdingAPI.AssemblyRewriters/RewriteHelper.cs new file mode 100644 index 00000000..cfb330dd --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/RewriteHelper.cs @@ -0,0 +1,94 @@ +using System; +using System.Linq; +using System.Reflection; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace StardewModdingAPI.AssemblyRewriters +{ + /// Provides helper methods for field rewriters. + internal static class RewriteHelper + { + /********* + ** Public methods + *********/ + /// Get the field reference from an instruction if it matches. + /// The IL instruction. + public static FieldReference AsFieldReference(Instruction instruction) + { + return instruction.OpCode == OpCodes.Ldfld || instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld + ? (FieldReference)instruction.Operand + : null; + } + + /// Get the method reference from an instruction if it matches. + /// The IL instruction. + public static MethodReference AsMethodReference(Instruction instruction) + { + return instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Callvirt + ? (MethodReference)instruction.Operand + : null; + } + + /// Get whether a type matches a type reference. + /// The defined type. + /// The type reference. + public static bool IsSameType(Type type, TypeReference reference) + { + // same namespace & name + if (type.Namespace != reference.Namespace || type.Name != reference.Name) + return false; + + // same generic parameters + if (type.IsGenericType) + { + if (!reference.IsGenericInstance) + return false; + + Type[] defGenerics = type.GetGenericArguments(); + TypeReference[] refGenerics = ((GenericInstanceType)reference).GenericArguments.ToArray(); + if (defGenerics.Length != refGenerics.Length) + return false; + for (int i = 0; i < defGenerics.Length; i++) + { + if (!RewriteHelper.IsSameType(defGenerics[i], refGenerics[i])) + return false; + } + } + + return true; + } + + /// Get whether a method definition matches the signature expected by a method reference. + /// The method definition. + /// The method reference. + public static 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.IsSameType(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. + public static bool HasMatchingSignature(Type type, MethodReference reference) + { + return type + .GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public) + .Any(method => RewriteHelper.HasMatchingSignature(method, reference)); + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs index 31f9a40f..a715e07b 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs @@ -2,12 +2,12 @@ using System; using System.Reflection; using Mono.Cecil; using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; +using StardewModdingAPI.AssemblyRewriters.Finders; namespace StardewModdingAPI.AssemblyRewriters.Rewriters { /// Rewrites references to one field with another. - public class FieldReplaceRewriter : BaseFieldRewriter + public class FieldReplaceRewriter : FieldFinder, IInstructionRewriter { /********* ** Properties @@ -15,20 +15,10 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters /// The type whose field to which references should be rewritten. private readonly Type Type; - /// The field name to rewrite. - private readonly string FromFieldName; - /// The new field name to reference. private readonly string ToFieldName; - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } - - /********* ** Public methods *********/ @@ -38,38 +28,25 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters /// The new field name to reference. /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). public FieldReplaceRewriter(Type type, string fromFieldName, string toFieldName, string nounPhrase = null) + : base(type.FullName, fromFieldName, nounPhrase) { this.Type = type; - this.FromFieldName = fromFieldName; this.ToFieldName = toFieldName; - this.NounPhrase = nounPhrase ?? $"{type.Name}.{fromFieldName} field"; } /********* ** Protected methods *********/ - /// Get whether a field reference should be rewritten. - /// The IL instruction. - /// The field reference. - /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) - { - return - fieldRef.DeclaringType.FullName == this.Type.FullName - && fieldRef.Name == this.FromFieldName; - } - - /// Rewrite a method for compatibility. + /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. - /// The instruction which references the field. - /// The field reference invoked by the . + /// The instruction to rewrite. /// Metadata for mapping assemblies to the current platform. - protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap) + public void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap) { FieldInfo field = this.Type.GetField(this.ToFieldName); - if(field == null) + if (field == null) throw new InvalidOperationException($"The {this.Type.FullName} class doesn't have a {this.ToFieldName} field."); FieldReference newRef = module.Import(field); cil.Replace(instruction, cil.Create(instruction.OpCode, newRef)); diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs index caf0a16c..62e24559 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs @@ -1,12 +1,12 @@ using System; using Mono.Cecil; using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; +using StardewModdingAPI.AssemblyRewriters.Finders; namespace StardewModdingAPI.AssemblyRewriters.Rewriters { /// Rewrites field references into property references. - public class FieldToPropertyRewriter : BaseFieldRewriter + public class FieldToPropertyRewriter : FieldFinder, IInstructionRewriter { /********* ** Properties @@ -18,13 +18,6 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters private readonly string FieldName; - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } - - /********* ** Public methods *********/ @@ -33,34 +26,22 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters /// The field name to rewrite. /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). public FieldToPropertyRewriter(Type type, string fieldName, string nounPhrase = null) + : base(type.FullName, fieldName, nounPhrase) { this.Type = type; this.FieldName = fieldName; - this.NounPhrase = nounPhrase ?? $"{type.Name}.{fieldName} field"; } /********* ** Protected methods *********/ - /// Get whether a field reference should be rewritten. - /// The IL instruction. - /// The field reference. - /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) - { - return - fieldRef.DeclaringType.FullName == this.Type.FullName - && fieldRef.Name == this.FieldName; - } - - /// Rewrite a method for compatibility. + /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. - /// The instruction which references the field. - /// The field reference invoked by the . + /// The instruction to rewrite. /// Metadata for mapping assemblies to the current platform. - protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap) + public void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap) { string methodPrefix = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld ? "get" : "set"; MethodReference propertyRef = module.Import(this.Type.GetMethod($"{methodPrefix}_{this.FieldName}")); diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs index 9c19f473..9b895056 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs @@ -1,12 +1,11 @@ using System; using Mono.Cecil; using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; namespace StardewModdingAPI.AssemblyRewriters.Rewriters { /// Rewrites method references from one parent type to another if the signatures match. - public class MethodParentRewriter : BaseMethodRewriter + public class MethodParentRewriter : IInstructionRewriter { /********* ** Properties @@ -25,7 +24,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters ** Accessors *********/ /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } + public string NounPhrase { get; } /********* @@ -48,26 +47,27 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters /********* ** Protected methods *********/ - /// Get whether a method reference should be rewritten. + /// Get whether a CIL instruction matches. /// The IL instruction. - /// The method reference. /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) + public bool IsMatch(Instruction instruction, bool platformChanged) { + MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); return - (!this.OnlyIfPlatformChanged || platformChanged) + methodRef != null + && (platformChanged || !this.OnlyIfPlatformChanged) && methodRef.DeclaringType.FullName == this.FromType.FullName - && this.HasMatchingSignature(this.ToType, methodRef); + && RewriteHelper.HasMatchingSignature(this.ToType, methodRef); } - /// Rewrite a method for compatibility. + /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. - /// The instruction which calls the method. - /// The method reference invoked by the . + /// The instruction to rewrite. /// Metadata for mapping assemblies to the current platform. - protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, MethodReference methodRef, PlatformAssemblyMap assemblyMap) + public void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap) { + MethodReference methodRef = (MethodReference)instruction.Operand; methodRef.DeclaringType = module.Import(this.ToType); } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 0136c39b..09fd79ed 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -70,11 +70,7 @@ - - - - - + diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index 9a7fba84..52be6c05 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -143,7 +143,7 @@ namespace StardewModdingAPI return new IInstructionFinder[] { // changes in Stardew Valley 1.2 (that don't have rewriters) - new FieldFinder("StardewValley.Item", "set_Name", isStatic: false), + new FieldFinder("StardewValley.Item", "set_Name"), // APIs removed in SMAPI 1.9 new TypeFinder("StardewModdingAPI.Advanced.ConfigFile"), -- cgit From 06f5e92b88fac190f94690f1580775449014e411 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 25 Mar 2017 22:08:48 -0400 Subject: minor cleanup --- .../Finders/EventFinder.cs | 5 +++-- .../Finders/MethodFinder.cs | 5 +++-- .../Finders/TypeFinder.cs | 25 +++++++++++----------- .../Rewriters/FieldReplaceRewriter.cs | 4 ---- .../Rewriters/FieldToPropertyRewriter.cs | 4 ---- .../Rewriters/MethodParentRewriter.cs | 4 ---- 6 files changed, 19 insertions(+), 28 deletions(-) (limited to 'src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs index 9d0184c6..848e54ff 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs @@ -29,11 +29,12 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /// Construct an instance. /// The full type name for which to find references. /// The event name for which to find references. - public EventFinder(string fullTypeName, string eventName) + /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). + public EventFinder(string fullTypeName, string eventName, string nounPhrase = null) { this.FullTypeName = fullTypeName; this.EventName = eventName; - this.NounPhrase = $"obsolete {fullTypeName}.{eventName} event"; + this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{eventName} event"; } diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs index bea549ee..d174bacd 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs @@ -29,11 +29,12 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /// Construct an instance. /// The full type name for which to find references. /// The method name for which to find references. - public MethodFinder(string fullTypeName, string methodName) + /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). + public MethodFinder(string fullTypeName, string methodName, string nounPhrase = null) { this.FullTypeName = fullTypeName; this.MethodName = methodName; - this.NounPhrase = $"obsolete {fullTypeName}.{methodName} method"; + this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{methodName} method"; } diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs index ba8e7102..8f492d5f 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs @@ -5,7 +5,7 @@ using Mono.Cecil.Cil; namespace StardewModdingAPI.AssemblyRewriters.Finders { /// Finds CIL instructions that reference a given type. - public sealed class TypeFinder : IInstructionFinder + public class TypeFinder : IInstructionFinder { /********* ** Accessors @@ -26,10 +26,11 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders *********/ /// Construct an instance. /// The full type name to match. - public TypeFinder(string fullTypeName) + /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). + public TypeFinder(string fullTypeName, string nounPhrase = null) { this.FullTypeName = fullTypeName; - this.NounPhrase = $"obsolete {fullTypeName} type"; + this.NounPhrase = nounPhrase ?? $"{fullTypeName} type"; } /// Get whether a CIL instruction matches. @@ -40,22 +41,22 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders string fullName = this.FullTypeName; // field reference - if (instruction.OpCode == OpCodes.Ldfld || instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld) + FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction); + if (fieldRef != null) { - FieldReference field = (FieldReference)instruction.Operand; return - field.DeclaringType.FullName == fullName // field on target class - || field.FieldType.FullName == fullName; // field value is target class + fieldRef.DeclaringType.FullName == fullName // field on target class + || fieldRef.FieldType.FullName == fullName; // field value is target class } // method reference - if (instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Callvirt) + MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); + if (methodRef != null) { - MethodReference method = (MethodReference)instruction.Operand; return - method.DeclaringType.FullName == fullName // method on target class - || method.ReturnType.FullName == fullName // method returns target class - || method.Parameters.Any(p => p.ParameterType.FullName == fullName); // method parameters + methodRef.DeclaringType.FullName == fullName // method on target class + || methodRef.ReturnType.FullName == fullName // method returns target class + || methodRef.Parameters.Any(p => p.ParameterType.FullName == fullName); // method parameters } return false; diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs index a715e07b..ffd22e7c 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs @@ -34,10 +34,6 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters this.ToFieldName = toFieldName; } - - /********* - ** Protected methods - *********/ /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs index 62e24559..f2f99cc1 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs @@ -32,10 +32,6 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters this.FieldName = fieldName; } - - /********* - ** Protected methods - *********/ /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs index 9b895056..24d4dff9 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs @@ -43,10 +43,6 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters this.OnlyIfPlatformChanged = onlyIfPlatformChanged; } - - /********* - ** Protected methods - *********/ /// Get whether a CIL instruction matches. /// The IL instruction. /// Whether the mod was compiled on a different platform. -- cgit From 85ed48809032fdbb8461ce4c34acfbe06f68652b Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 26 Mar 2017 19:01:35 -0400 Subject: merge CIL finders & rewriters into one interface (#254) --- .../Finders/EventFinder.cs | 22 ++++++++++++-- .../Finders/FieldFinder.cs | 22 ++++++++++++-- .../Finders/MethodFinder.cs | 22 ++++++++++++-- .../Finders/TypeFinder.cs | 26 ++++++++++++++-- .../IInstructionFinder.cs | 23 -------------- .../IInstructionRewriter.cs | 16 ++++++++-- .../IncompatibleInstructionException.cs | 35 ++++++++++++++++++++++ .../Rewriters/FieldReplaceRewriter.cs | 28 +++++++++-------- .../Rewriters/FieldToPropertyRewriter.cs | 11 +++++-- .../Rewriters/MethodParentRewriter.cs | 35 ++++++++++++++-------- .../StardewModdingAPI.AssemblyRewriters.csproj | 2 +- src/StardewModdingAPI/Constants.cs | 23 +++++++------- src/StardewModdingAPI/Framework/AssemblyLoader.cs | 33 ++++++++++---------- .../Framework/IncompatibleInstructionException.cs | 27 ----------------- src/StardewModdingAPI/Program.cs | 1 + src/StardewModdingAPI/StardewModdingAPI.csproj | 1 - 16 files changed, 203 insertions(+), 124 deletions(-) delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/IInstructionFinder.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/IncompatibleInstructionException.cs delete mode 100644 src/StardewModdingAPI/Framework/IncompatibleInstructionException.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs index 848e54ff..bcceee32 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs @@ -3,8 +3,8 @@ using Mono.Cecil.Cil; namespace StardewModdingAPI.AssemblyRewriters.Finders { - /// Finds CIL instructions that reference a given event. - public sealed class EventFinder : IInstructionFinder + /// Finds incompatible CIL instructions that reference a given event and throws an . + public class EventFinder : IInstructionRewriter { /********* ** Properties @@ -37,6 +37,22 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{eventName} event"; } + /// 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. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + if (!this.IsMatch(instruction, platformChanged)) + return false; + + throw new IncompatibleInstructionException(this.NounPhrase); + } + /********* ** Protected methods @@ -44,7 +60,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /// Get whether a CIL instruction matches. /// The IL instruction. /// Whether the mod was compiled on a different platform. - public bool IsMatch(Instruction instruction, bool platformChanged) + protected bool IsMatch(Instruction instruction, bool platformChanged) { MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); return diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs index 068119b8..cdfc3bd5 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs @@ -3,8 +3,8 @@ using Mono.Cecil.Cil; namespace StardewModdingAPI.AssemblyRewriters.Finders { - /// Finds CIL instructions that reference a given field. - public class FieldFinder : IInstructionFinder + /// Finds incompatible CIL instructions that reference a given field and throws an . + public class FieldFinder : IInstructionRewriter { /********* ** Properties @@ -37,6 +37,22 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{fieldName} field"; } + /// 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. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public virtual bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + if (!this.IsMatch(instruction, platformChanged)) + return false; + + throw new IncompatibleInstructionException(this.NounPhrase); + } + /********* ** Protected methods @@ -44,7 +60,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /// Get whether a CIL instruction matches. /// The IL instruction. /// Whether the mod was compiled on a different platform. - public bool IsMatch(Instruction instruction, bool platformChanged) + protected bool IsMatch(Instruction instruction, bool platformChanged) { FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction); return diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs index d174bacd..2efcbb0f 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs @@ -3,8 +3,8 @@ using Mono.Cecil.Cil; namespace StardewModdingAPI.AssemblyRewriters.Finders { - /// Finds CIL instructions that reference a given method. - public class MethodFinder : IInstructionFinder + /// Finds incompatible CIL instructions that reference a given method and throws an . + public class MethodFinder : IInstructionRewriter { /********* ** Properties @@ -37,6 +37,22 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{methodName} method"; } + /// 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. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + if (!this.IsMatch(instruction, platformChanged)) + return false; + + throw new IncompatibleInstructionException(this.NounPhrase); + } + /********* ** Protected methods @@ -44,7 +60,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /// Get whether a CIL instruction matches. /// The IL instruction. /// Whether the mod was compiled on a different platform. - public bool IsMatch(Instruction instruction, bool platformChanged) + protected bool IsMatch(Instruction instruction, bool platformChanged) { MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); return diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs index 8f492d5f..96cbb229 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs @@ -4,8 +4,8 @@ using Mono.Cecil.Cil; namespace StardewModdingAPI.AssemblyRewriters.Finders { - /// Finds CIL instructions that reference a given type. - public class TypeFinder : IInstructionFinder + /// Finds incompatible CIL instructions that reference a given type and throws an . + public class TypeFinder : IInstructionRewriter { /********* ** Accessors @@ -33,10 +33,30 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders this.NounPhrase = nounPhrase ?? $"{fullTypeName} type"; } + /// 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. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public virtual bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + if (!this.IsMatch(instruction, platformChanged)) + return false; + + throw new IncompatibleInstructionException(this.NounPhrase); + } + + + /********* + ** Protected methods + *********/ /// Get whether a CIL instruction matches. /// The IL instruction. /// Whether the mod was compiled on a different platform. - public bool IsMatch(Instruction instruction, bool platformChanged) + protected bool IsMatch(Instruction instruction, bool platformChanged) { string fullName = this.FullTypeName; diff --git a/src/StardewModdingAPI.AssemblyRewriters/IInstructionFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/IInstructionFinder.cs deleted file mode 100644 index cc3006b9..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/IInstructionFinder.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Mono.Cecil.Cil; - -namespace StardewModdingAPI.AssemblyRewriters -{ - /// Finds CIL instructions considered incompatible. - public interface IInstructionFinder - { - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - string NounPhrase { get; } - - - /********* - ** Methods - *********/ - /// Get whether a CIL instruction matches. - /// The IL instruction. - /// Whether the mod was compiled on a different platform. - bool IsMatch(Instruction instruction, bool platformChanged); - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/IInstructionRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/IInstructionRewriter.cs index b230f227..3a7b1365 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/IInstructionRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/IInstructionRewriter.cs @@ -3,9 +3,16 @@ using Mono.Cecil.Cil; namespace StardewModdingAPI.AssemblyRewriters { - /// Rewrites a CIL instruction for compatibility. - public interface IInstructionRewriter : IInstructionFinder + /// Rewrites CIL instructions for compatibility. + public interface IInstructionRewriter { + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the rewriter matches. + string NounPhrase { get; } + + /********* ** Methods *********/ @@ -14,6 +21,9 @@ namespace StardewModdingAPI.AssemblyRewriters /// The CIL rewriter. /// The instruction to rewrite. /// Metadata for mapping assemblies to the current platform. - void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap); + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged); } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/IncompatibleInstructionException.cs b/src/StardewModdingAPI.AssemblyRewriters/IncompatibleInstructionException.cs new file mode 100644 index 00000000..f7e6bd8f --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/IncompatibleInstructionException.cs @@ -0,0 +1,35 @@ +using System; + +namespace StardewModdingAPI.AssemblyRewriters +{ + /// An exception raised when an incompatible instruction is found while loading a mod assembly. + public class IncompatibleInstructionException : Exception + { + /********* + ** Accessors + *********/ + /// A brief noun phrase which describes the incompatible instruction that was found. + public string NounPhrase { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// A brief noun phrase which describes the incompatible instruction that was found. + public IncompatibleInstructionException(string nounPhrase) + : base($"Found an incompatible CIL instruction ({nounPhrase}).") + { + this.NounPhrase = nounPhrase; + } + + /// Construct an instance. + /// A brief noun phrase which describes the incompatible instruction that was found. + /// A message which describes the error. + public IncompatibleInstructionException(string nounPhrase, string message) + : base(message) + { + this.NounPhrase = nounPhrase; + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs index ffd22e7c..95663c49 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs @@ -7,16 +7,13 @@ using StardewModdingAPI.AssemblyRewriters.Finders; namespace StardewModdingAPI.AssemblyRewriters.Rewriters { /// Rewrites references to one field with another. - public class FieldReplaceRewriter : FieldFinder, IInstructionRewriter + public class FieldReplaceRewriter : FieldFinder { /********* ** Properties *********/ - /// The type whose field to which references should be rewritten. - private readonly Type Type; - - /// The new field name to reference. - private readonly string ToFieldName; + /// The new field to reference. + private readonly FieldInfo ToField; /********* @@ -30,8 +27,9 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters public FieldReplaceRewriter(Type type, string fromFieldName, string toFieldName, string nounPhrase = null) : base(type.FullName, fromFieldName, nounPhrase) { - this.Type = type; - this.ToFieldName = toFieldName; + this.ToField = type.GetField(toFieldName); + if (this.ToField == null) + throw new InvalidOperationException($"The {type.FullName} class doesn't have a {toFieldName} field."); } /// Rewrite a CIL instruction for compatibility. @@ -39,13 +37,17 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters /// 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) + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public override bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { - FieldInfo field = this.Type.GetField(this.ToFieldName); - if (field == null) - throw new InvalidOperationException($"The {this.Type.FullName} class doesn't have a {this.ToFieldName} field."); - FieldReference newRef = module.Import(field); + if (!this.IsMatch(instruction, platformChanged)) + return false; + + FieldReference newRef = module.Import(this.ToField); cil.Replace(instruction, cil.Create(instruction.OpCode, newRef)); + return true; } } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs index f2f99cc1..a25f3fef 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs @@ -6,7 +6,7 @@ using StardewModdingAPI.AssemblyRewriters.Finders; namespace StardewModdingAPI.AssemblyRewriters.Rewriters { /// Rewrites field references into property references. - public class FieldToPropertyRewriter : FieldFinder, IInstructionRewriter + public class FieldToPropertyRewriter : FieldFinder { /********* ** Properties @@ -37,11 +37,18 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters /// 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) + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public override bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { + if (!this.IsMatch(instruction, platformChanged)) + return false; + string methodPrefix = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld ? "get" : "set"; MethodReference propertyRef = module.Import(this.Type.GetMethod($"{methodPrefix}_{this.FieldName}")); cil.Replace(instruction, cil.Create(OpCodes.Call, propertyRef)); + return true; } } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs index 24d4dff9..3ec8c704 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs @@ -43,10 +43,32 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters this.OnlyIfPlatformChanged = onlyIfPlatformChanged; } + /// 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. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + if (!this.IsMatch(instruction, platformChanged)) + return false; + + MethodReference methodRef = (MethodReference)instruction.Operand; + methodRef.DeclaringType = module.Import(this.ToType); + return true; + } + + + /********* + ** Protected methods + *********/ /// Get whether a CIL instruction matches. /// The IL instruction. /// Whether the mod was compiled on a different platform. - public bool IsMatch(Instruction instruction, bool platformChanged) + protected bool IsMatch(Instruction instruction, bool platformChanged) { MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); return @@ -55,16 +77,5 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters && methodRef.DeclaringType.FullName == this.FromType.FullName && RewriteHelper.HasMatchingSignature(this.ToType, methodRef); } - - /// 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; - methodRef.DeclaringType = module.Import(this.ToType); - } } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 09fd79ed..3c3acde3 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -70,8 +70,8 @@ + - diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index 52be6c05..de0eab57 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -137,12 +137,15 @@ namespace StardewModdingAPI return new PlatformAssemblyMap(targetPlatform, removeAssemblyReferences, targetAssemblies); } - /// Get finders which match incompatible CIL instructions in mod assemblies. - internal static IEnumerable GetIncompatibilityFinders() + /// Get rewriters which detect or fix incompatible CIL instructions in mod assemblies. + internal static IEnumerable GetRewriters() { - return new IInstructionFinder[] + return new IInstructionRewriter[] { - // changes in Stardew Valley 1.2 (that don't have rewriters) + /**** + ** Finders throw an exception when incompatible code is found. + ****/ + // changes in Stardew Valley 1.2 (with no rewriters) new FieldFinder("StardewValley.Item", "set_Name"), // APIs removed in SMAPI 1.9 @@ -161,15 +164,11 @@ namespace StardewModdingAPI new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPostRenderHudEventNoCheck"), new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPostRenderGuiEventNoCheck"), new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPreRenderHudEventNoCheck"), - new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPreRenderGuiEventNoCheck") - }; - } + new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPreRenderGuiEventNoCheck"), - /// Get rewriters which fix incompatible CIL instructions in mod assemblies. - internal static IEnumerable GetRewriters() - { - return new IInstructionRewriter[] - { + /**** + ** Rewriters change CIL as needed to fix incompatible code + ****/ // crossplatform new MethodParentRewriter(typeof(SpriteBatch), typeof(SpriteBatchWrapper), onlyIfPlatformChanged: true), diff --git a/src/StardewModdingAPI/Framework/AssemblyLoader.cs b/src/StardewModdingAPI/Framework/AssemblyLoader.cs index aee0bbb3..5d00c525 100644 --- a/src/StardewModdingAPI/Framework/AssemblyLoader.cs +++ b/src/StardewModdingAPI/Framework/AssemblyLoader.cs @@ -193,33 +193,30 @@ namespace StardewModdingAPI.Framework this.ChangeTypeScope(type); } - // find incompatible instructions + // find (and optionally rewrite) incompatible instructions bool anyRewritten = false; - IInstructionFinder[] finders = Constants.GetIncompatibilityFinders().ToArray(); IInstructionRewriter[] rewriters = Constants.GetRewriters().ToArray(); foreach (MethodDefinition method in this.GetMethods(module)) { ILProcessor cil = method.Body.GetILProcessor(); foreach (Instruction instruction in cil.Body.Instructions.ToArray()) { - // throw exception if instruction is incompatible but can't be rewritten - IInstructionFinder finder = finders.FirstOrDefault(p => p.IsMatch(instruction, platformChanged)); - if (finder != null) - { - if (!assumeCompatible) - throw new IncompatibleInstructionException(finder.NounPhrase, $"Found an incompatible CIL instruction ({finder.NounPhrase}) while loading assembly {assembly.Name.Name}."); - this.LogOnce(this.Monitor, loggedMessages, $"Found an incompatible CIL instruction ({finder.NounPhrase}) while loading assembly {assembly.Name.Name}, but SMAPI is configured to allow it anyway. The mod may crash or behave unexpectedly.", LogLevel.Warn); - } - - // rewrite instruction if needed foreach (IInstructionRewriter rewriter in rewriters) { - if (!rewriter.IsMatch(instruction, platformChanged)) - continue; - - this.LogOnce(this.Monitor, loggedMessages, $"Rewriting {assembly.Name.Name} to fix {rewriter.NounPhrase}..."); - rewriter.Rewrite(module, cil, instruction, this.AssemblyMap); - anyRewritten = true; + try + { + if (rewriter.Rewrite(module, cil, instruction, this.AssemblyMap, platformChanged)) + { + this.LogOnce(this.Monitor, loggedMessages, $"Rewrote {assembly.Name.Name} to fix {rewriter.NounPhrase}..."); + anyRewritten = true; + } + } + catch (IncompatibleInstructionException) + { + if (!assumeCompatible) + throw new IncompatibleInstructionException(rewriter.NounPhrase, $"Found an incompatible CIL instruction ({rewriter.NounPhrase}) while loading assembly {assembly.Name.Name}."); + this.LogOnce(this.Monitor, loggedMessages, $"Found an incompatible CIL instruction ({rewriter.NounPhrase}) while loading assembly {assembly.Name.Name}, but SMAPI is configured to allow it anyway. The mod may crash or behave unexpectedly.", LogLevel.Warn); + } } } } diff --git a/src/StardewModdingAPI/Framework/IncompatibleInstructionException.cs b/src/StardewModdingAPI/Framework/IncompatibleInstructionException.cs deleted file mode 100644 index affe2cb3..00000000 --- a/src/StardewModdingAPI/Framework/IncompatibleInstructionException.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; - -namespace StardewModdingAPI.Framework -{ - /// An exception raised when an incompatible instruction is found while loading a mod assembly. - internal class IncompatibleInstructionException : Exception - { - /********* - ** Accessors - *********/ - /// A brief noun phrase which describes the incompatible instruction that was found. - public string NounPhrase { get; } - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// A brief noun phrase which describes the incompatible instruction that was found. - /// A message which describes the error. - public IncompatibleInstructionException(string nounPhrase, string message) - : base(message) - { - this.NounPhrase = nounPhrase; - } - } -} diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index ac646b1f..25605148 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -12,6 +12,7 @@ using System.Windows.Forms; #endif using Microsoft.Xna.Framework.Graphics; using Newtonsoft.Json; +using StardewModdingAPI.AssemblyRewriters; using StardewModdingAPI.Events; using StardewModdingAPI.Framework; using StardewModdingAPI.Framework.Logging; diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index 091b3d90..bcd0c390 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -150,7 +150,6 @@ - -- cgit From 8bf3ef118a822afd1c7d7f80f6cf6eaeed346167 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 26 Mar 2017 19:17:48 -0400 Subject: add support for rewriting method definitions (#254) --- .../Finders/EventFinder.cs | 19 +++++++++--- .../Finders/FieldFinder.cs | 17 ++++++++-- .../Finders/MethodFinder.cs | 17 ++++++++-- .../Finders/TypeFinder.cs | 36 ++++++++++++++++++++-- .../IInstructionRewriter.cs | 9 ++++++ .../Rewriters/FieldReplaceRewriter.cs | 2 +- .../Rewriters/FieldToPropertyRewriter.cs | 2 +- .../Rewriters/MethodParentRewriter.cs | 12 ++++++++ src/StardewModdingAPI/Framework/AssemblyLoader.cs | 20 ++++++++++++ 9 files changed, 119 insertions(+), 15 deletions(-) (limited to 'src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs index bcceee32..c0051469 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs @@ -37,6 +37,18 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{eventName} event"; } + /// Rewrite a method definition for compatibility. + /// The module being rewritten. + /// The method definition to rewrite. + /// Metadata for mapping assemblies to the current platform. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public virtual bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + return false; + } + /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. @@ -45,9 +57,9 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /// Whether the mod was compiled on a different platform. /// Returns whether the instruction was rewritten. /// The CIL instruction is not compatible, and can't be rewritten. - public bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) + public virtual bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { - if (!this.IsMatch(instruction, platformChanged)) + if (!this.IsMatch(instruction)) return false; throw new IncompatibleInstructionException(this.NounPhrase); @@ -59,8 +71,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders *********/ /// Get whether a CIL instruction matches. /// The IL instruction. - /// Whether the mod was compiled on a different platform. - protected bool IsMatch(Instruction instruction, bool platformChanged) + protected bool IsMatch(Instruction instruction) { MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); return diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs index cdfc3bd5..b44883e9 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs @@ -37,6 +37,18 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{fieldName} field"; } + /// Rewrite a method definition for compatibility. + /// The module being rewritten. + /// The method definition to rewrite. + /// Metadata for mapping assemblies to the current platform. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public virtual bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + return false; + } + /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. @@ -47,7 +59,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /// The CIL instruction is not compatible, and can't be rewritten. public virtual bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { - if (!this.IsMatch(instruction, platformChanged)) + if (!this.IsMatch(instruction)) return false; throw new IncompatibleInstructionException(this.NounPhrase); @@ -59,8 +71,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders *********/ /// Get whether a CIL instruction matches. /// The IL instruction. - /// Whether the mod was compiled on a different platform. - protected bool IsMatch(Instruction instruction, bool platformChanged) + protected bool IsMatch(Instruction instruction) { FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction); return diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs index 2efcbb0f..19dda58a 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs @@ -37,6 +37,18 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{methodName} method"; } + /// Rewrite a method definition for compatibility. + /// The module being rewritten. + /// The method definition to rewrite. + /// Metadata for mapping assemblies to the current platform. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public virtual bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + return false; + } + /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. @@ -47,7 +59,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /// The CIL instruction is not compatible, and can't be rewritten. public bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { - if (!this.IsMatch(instruction, platformChanged)) + if (!this.IsMatch(instruction)) return false; throw new IncompatibleInstructionException(this.NounPhrase); @@ -59,8 +71,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders *********/ /// Get whether a CIL instruction matches. /// The IL instruction. - /// Whether the mod was compiled on a different platform. - protected bool IsMatch(Instruction instruction, bool platformChanged) + protected bool IsMatch(Instruction instruction) { MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); return diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs index 96cbb229..0e4d6824 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs @@ -33,6 +33,21 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders this.NounPhrase = nounPhrase ?? $"{fullTypeName} type"; } + /// Rewrite a method definition for compatibility. + /// The module being rewritten. + /// The method definition to rewrite. + /// Metadata for mapping assemblies to the current platform. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public virtual bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + if (!this.IsMatch(method)) + return false; + + throw new IncompatibleInstructionException(this.NounPhrase); + } + /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. @@ -43,7 +58,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /// The CIL instruction is not compatible, and can't be rewritten. public virtual bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { - if (!this.IsMatch(instruction, platformChanged)) + if (!this.IsMatch(instruction)) return false; throw new IncompatibleInstructionException(this.NounPhrase); @@ -53,10 +68,25 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /********* ** Protected methods *********/ + /// Get whether a CIL instruction matches. + /// The method deifnition. + protected bool IsMatch(MethodDefinition method) + { + if (method.ReturnType.FullName == this.FullTypeName) + return true; + + foreach (VariableDefinition variable in method.Body.Variables) + { + if (variable.VariableType.FullName == this.FullTypeName) + return true; + } + + return false; + } + /// Get whether a CIL instruction matches. /// The IL instruction. - /// Whether the mod was compiled on a different platform. - protected bool IsMatch(Instruction instruction, bool platformChanged) + protected bool IsMatch(Instruction instruction) { string fullName = this.FullTypeName; diff --git a/src/StardewModdingAPI.AssemblyRewriters/IInstructionRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/IInstructionRewriter.cs index 3a7b1365..2f16b23d 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/IInstructionRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/IInstructionRewriter.cs @@ -16,6 +16,15 @@ namespace StardewModdingAPI.AssemblyRewriters /********* ** Methods *********/ + /// Rewrite a method definition for compatibility. + /// The module being rewritten. + /// The method definition to rewrite. + /// Metadata for mapping assemblies to the current platform. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged); + /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs index 95663c49..73844073 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs @@ -42,7 +42,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters /// The CIL instruction is not compatible, and can't be rewritten. public override bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { - if (!this.IsMatch(instruction, platformChanged)) + if (!this.IsMatch(instruction)) return false; FieldReference newRef = module.Import(this.ToField); diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs index a25f3fef..3f57042d 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs @@ -42,7 +42,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters /// The CIL instruction is not compatible, and can't be rewritten. public override bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { - if (!this.IsMatch(instruction, platformChanged)) + if (!this.IsMatch(instruction)) return false; string methodPrefix = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld ? "get" : "set"; diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs index 3ec8c704..035ef211 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs @@ -43,6 +43,18 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters this.OnlyIfPlatformChanged = onlyIfPlatformChanged; } + /// Rewrite a method definition for compatibility. + /// The module being rewritten. + /// The method definition to rewrite. + /// Metadata for mapping assemblies to the current platform. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + return false; + } + /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. diff --git a/src/StardewModdingAPI/Framework/AssemblyLoader.cs b/src/StardewModdingAPI/Framework/AssemblyLoader.cs index 5d00c525..f6fe89f5 100644 --- a/src/StardewModdingAPI/Framework/AssemblyLoader.cs +++ b/src/StardewModdingAPI/Framework/AssemblyLoader.cs @@ -198,6 +198,26 @@ namespace StardewModdingAPI.Framework IInstructionRewriter[] rewriters = Constants.GetRewriters().ToArray(); foreach (MethodDefinition method in this.GetMethods(module)) { + // check method definition + foreach (IInstructionRewriter rewriter in rewriters) + { + try + { + if (rewriter.Rewrite(module, method, this.AssemblyMap, platformChanged)) + { + this.LogOnce(this.Monitor, loggedMessages, $"Rewrote {assembly.Name.Name} to fix {rewriter.NounPhrase}..."); + anyRewritten = true; + } + } + catch (IncompatibleInstructionException) + { + if (!assumeCompatible) + throw new IncompatibleInstructionException(rewriter.NounPhrase, $"Found an incompatible CIL instruction ({rewriter.NounPhrase}) while loading assembly {assembly.Name.Name}."); + this.LogOnce(this.Monitor, loggedMessages, $"Found an incompatible CIL instruction ({rewriter.NounPhrase}) while loading assembly {assembly.Name.Name}, but SMAPI is configured to allow it anyway. The mod may crash or behave unexpectedly.", LogLevel.Warn); + } + } + + // check CIL instructions ILProcessor cil = method.Body.GetILProcessor(); foreach (Instruction instruction in cil.Body.Instructions.ToArray()) { -- cgit