summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework/ModLoading/Finders
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI/Framework/ModLoading/Finders')
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs40
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs53
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs40
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs40
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs48
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs49
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs51
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs120
8 files changed, 154 insertions, 287 deletions
diff --git a/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs
index 898bafb4..e1476b73 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs
@@ -1,10 +1,12 @@
+using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
+using StardewModdingAPI.Framework.ModLoading.Framework;
namespace StardewModdingAPI.Framework.ModLoading.Finders
{
/// <summary>Finds incompatible CIL instructions that reference a given event.</summary>
- internal class EventFinder : IInstructionHandler
+ internal class EventFinder : BaseInstructionHandler
{
/*********
** Fields
@@ -20,13 +22,6 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/*********
- ** Accessors
- *********/
- /// <summary>A brief noun phrase indicating what the instruction finder matches.</summary>
- public string NounPhrase { get; }
-
-
- /*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
@@ -34,34 +29,25 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/// <param name="eventName">The event name for which to find references.</param>
/// <param name="result">The result to return for matching instructions.</param>
public EventFinder(string fullTypeName, string eventName, InstructionHandleResult result)
+ : base(defaultPhrase: $"{fullTypeName}.{eventName} event")
{
this.FullTypeName = fullTypeName;
this.EventName = eventName;
this.Result = result;
- this.NounPhrase = $"{fullTypeName}.{eventName} event";
}
- /// <summary>Perform the predefined logic for a method if applicable.</summary>
- /// <param name="module">The assembly module containing the instruction.</param>
- /// <param name="method">The method definition containing the instruction.</param>
- /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
- /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
- {
- return InstructionHandleResult.None;
- }
-
- /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
+ /// <summary>Rewrite a CIL instruction reference if needed.</summary>
/// <param name="module">The assembly module containing the instruction.</param>
/// <param name="cil">The CIL processor.</param>
- /// <param name="instruction">The instruction to handle.</param>
- /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
- /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
+ /// <param name="instruction">The CIL instruction to handle.</param>
+ /// <param name="replaceWith">Replaces the CIL instruction with a new one.</param>
+ /// <returns>Returns whether the instruction was changed.</returns>
+ public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, Action<Instruction> replaceWith)
{
- return this.IsMatch(instruction)
- ? this.Result
- : InstructionHandleResult.None;
+ if (!this.Flags.Contains(this.Result) && this.IsMatch(instruction))
+ this.MarkFlag(this.Result);
+
+ return false;
}
diff --git a/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs
index 606ca8b7..c157ed9b 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs
@@ -1,10 +1,12 @@
+using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
+using StardewModdingAPI.Framework.ModLoading.Framework;
namespace StardewModdingAPI.Framework.ModLoading.Finders
{
/// <summary>Finds incompatible CIL instructions that reference a given field.</summary>
- internal class FieldFinder : IInstructionHandler
+ internal class FieldFinder : BaseInstructionHandler
{
/*********
** Fields
@@ -20,13 +22,6 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/*********
- ** Accessors
- *********/
- /// <summary>A brief noun phrase indicating what the instruction finder matches.</summary>
- public string NounPhrase { get; }
-
-
- /*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
@@ -34,49 +29,25 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/// <param name="fieldName">The field name for which to find references.</param>
/// <param name="result">The result to return for matching instructions.</param>
public FieldFinder(string fullTypeName, string fieldName, InstructionHandleResult result)
+ : base(defaultPhrase: $"{fullTypeName}.{fieldName} field")
{
this.FullTypeName = fullTypeName;
this.FieldName = fieldName;
this.Result = result;
- this.NounPhrase = $"{fullTypeName}.{fieldName} field";
}
- /// <summary>Perform the predefined logic for a method if applicable.</summary>
- /// <param name="module">The assembly module containing the instruction.</param>
- /// <param name="method">The method definition containing the instruction.</param>
- /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
- /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
- {
- return InstructionHandleResult.None;
- }
-
- /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
+ /// <summary>Rewrite a CIL instruction reference if needed.</summary>
/// <param name="module">The assembly module containing the instruction.</param>
/// <param name="cil">The CIL processor.</param>
- /// <param name="instruction">The instruction to handle.</param>
- /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
- /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
+ /// <param name="instruction">The CIL instruction to handle.</param>
+ /// <param name="replaceWith">Replaces the CIL instruction with a new one.</param>
+ /// <returns>Returns whether the instruction was changed.</returns>
+ public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, Action<Instruction> replaceWith)
{
- return this.IsMatch(instruction)
- ? this.Result
- : InstructionHandleResult.None;
- }
-
+ if (!this.Flags.Contains(this.Result) && RewriteHelper.IsFieldReferenceTo(instruction, this.FullTypeName, this.FieldName))
+ this.MarkFlag(this.Result);
- /*********
- ** Protected methods
- *********/
- /// <summary>Get whether a CIL instruction matches.</summary>
- /// <param name="instruction">The IL instruction.</param>
- protected bool IsMatch(Instruction instruction)
- {
- FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
- return
- fieldRef != null
- && fieldRef.DeclaringType.FullName == this.FullTypeName
- && fieldRef.Name == this.FieldName;
+ return false;
}
}
}
diff --git a/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs
index 9ca246ff..82c93a7c 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs
@@ -1,10 +1,12 @@
+using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
+using StardewModdingAPI.Framework.ModLoading.Framework;
namespace StardewModdingAPI.Framework.ModLoading.Finders
{
/// <summary>Finds incompatible CIL instructions that reference a given method.</summary>
- internal class MethodFinder : IInstructionHandler
+ internal class MethodFinder : BaseInstructionHandler
{
/*********
** Fields
@@ -20,13 +22,6 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/*********
- ** Accessors
- *********/
- /// <summary>A brief noun phrase indicating what the instruction finder matches.</summary>
- public string NounPhrase { get; }
-
-
- /*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
@@ -34,34 +29,25 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/// <param name="methodName">The method name for which to find references.</param>
/// <param name="result">The result to return for matching instructions.</param>
public MethodFinder(string fullTypeName, string methodName, InstructionHandleResult result)
+ : base(defaultPhrase: $"{fullTypeName}.{methodName} method")
{
this.FullTypeName = fullTypeName;
this.MethodName = methodName;
this.Result = result;
- this.NounPhrase = $"{fullTypeName}.{methodName} method";
}
- /// <summary>Perform the predefined logic for a method if applicable.</summary>
- /// <param name="module">The assembly module containing the instruction.</param>
- /// <param name="method">The method definition containing the instruction.</param>
- /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
- /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
- {
- return InstructionHandleResult.None;
- }
-
- /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
+ /// <summary>Rewrite a CIL instruction reference if needed.</summary>
/// <param name="module">The assembly module containing the instruction.</param>
/// <param name="cil">The CIL processor.</param>
- /// <param name="instruction">The instruction to handle.</param>
- /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
- /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
- public InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
+ /// <param name="instruction">The CIL instruction to handle.</param>
+ /// <param name="replaceWith">Replaces the CIL instruction with a new one.</param>
+ /// <returns>Returns whether the instruction was changed.</returns>
+ public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, Action<Instruction> replaceWith)
{
- return this.IsMatch(instruction)
- ? this.Result
- : InstructionHandleResult.None;
+ if (!this.Flags.Contains(this.Result) && this.IsMatch(instruction))
+ this.MarkFlag(this.Result);
+
+ return false;
}
diff --git a/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs
index 0677aa88..c96d61a2 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs
@@ -1,10 +1,12 @@
+using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
+using StardewModdingAPI.Framework.ModLoading.Framework;
namespace StardewModdingAPI.Framework.ModLoading.Finders
{
/// <summary>Finds incompatible CIL instructions that reference a given property.</summary>
- internal class PropertyFinder : IInstructionHandler
+ internal class PropertyFinder : BaseInstructionHandler
{
/*********
** Fields
@@ -20,13 +22,6 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/*********
- ** Accessors
- *********/
- /// <summary>A brief noun phrase indicating what the instruction finder matches.</summary>
- public string NounPhrase { get; }
-
-
- /*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
@@ -34,34 +29,25 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/// <param name="propertyName">The property name for which to find references.</param>
/// <param name="result">The result to return for matching instructions.</param>
public PropertyFinder(string fullTypeName, string propertyName, InstructionHandleResult result)
+ : base(defaultPhrase: $"{fullTypeName}.{propertyName} property")
{
this.FullTypeName = fullTypeName;
this.PropertyName = propertyName;
this.Result = result;
- this.NounPhrase = $"{fullTypeName}.{propertyName} property";
}
- /// <summary>Perform the predefined logic for a method if applicable.</summary>
- /// <param name="module">The assembly module containing the instruction.</param>
- /// <param name="method">The method definition containing the instruction.</param>
- /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
- /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
- {
- return InstructionHandleResult.None;
- }
-
- /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
+ /// <summary>Rewrite a CIL instruction reference if needed.</summary>
/// <param name="module">The assembly module containing the instruction.</param>
/// <param name="cil">The CIL processor.</param>
- /// <param name="instruction">The instruction to handle.</param>
- /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
- /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
+ /// <param name="instruction">The CIL instruction to handle.</param>
+ /// <param name="replaceWith">Replaces the CIL instruction with a new one.</param>
+ /// <returns>Returns whether the instruction was changed.</returns>
+ public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, Action<Instruction> replaceWith)
{
- return this.IsMatch(instruction)
- ? this.Result
- : InstructionHandleResult.None;
+ if (!this.Flags.Contains(this.Result) && this.IsMatch(instruction))
+ this.MarkFlag(this.Result);
+
+ return false;
}
diff --git a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs
index 459e3210..a67cfa4f 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs
@@ -1,13 +1,15 @@
+using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
+using StardewModdingAPI.Framework.ModLoading.Framework;
namespace StardewModdingAPI.Framework.ModLoading.Finders
{
/// <summary>Finds references to a field, property, or method which returns a different type than the code expects.</summary>
/// <remarks>This implementation is purely heuristic. It should never return a false positive, but won't detect all cases.</remarks>
- internal class ReferenceToMemberWithUnexpectedTypeFinder : IInstructionHandler
+ internal class ReferenceToMemberWithUnexpectedTypeFinder : BaseInstructionHandler
{
/*********
** Fields
@@ -17,39 +19,23 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/*********
- ** Accessors
- *********/
- /// <summary>A brief noun phrase indicating what the instruction finder matches.</summary>
- public string NounPhrase { get; private set; } = "";
-
-
- /*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="validateReferencesToAssemblies">The assembly names to which to heuristically detect broken references.</param>
public ReferenceToMemberWithUnexpectedTypeFinder(string[] validateReferencesToAssemblies)
+ : base(defaultPhrase: "")
{
this.ValidateReferencesToAssemblies = new HashSet<string>(validateReferencesToAssemblies);
}
- /// <summary>Perform the predefined logic for a method if applicable.</summary>
- /// <param name="module">The assembly module containing the instruction.</param>
- /// <param name="method">The method definition containing the instruction.</param>
- /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
- /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
- {
- return InstructionHandleResult.None;
- }
-
- /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
+ /// <summary>Rewrite a CIL instruction reference if needed.</summary>
/// <param name="module">The assembly module containing the instruction.</param>
/// <param name="cil">The CIL processor.</param>
- /// <param name="instruction">The instruction to handle.</param>
- /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
- /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
+ /// <param name="instruction">The CIL instruction to handle.</param>
+ /// <param name="replaceWith">Replaces the CIL instruction with a new one.</param>
+ /// <returns>Returns whether the instruction was changed.</returns>
+ public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, Action<Instruction> replaceWith)
{
// field reference
FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
@@ -58,13 +44,13 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
// get target field
FieldDefinition targetField = fieldRef.DeclaringType.Resolve()?.Fields.FirstOrDefault(p => p.Name == fieldRef.Name);
if (targetField == null)
- return InstructionHandleResult.None;
+ return false;
// validate return type
if (!RewriteHelper.LooksLikeSameType(fieldRef.FieldType, targetField.FieldType))
{
- this.NounPhrase = $"reference to {fieldRef.DeclaringType.FullName}.{fieldRef.Name} (field returns {this.GetFriendlyTypeName(targetField.FieldType)}, not {this.GetFriendlyTypeName(fieldRef.FieldType)})";
- return InstructionHandleResult.NotCompatible;
+ this.MarkFlag(InstructionHandleResult.NotCompatible, $"reference to {fieldRef.DeclaringType.FullName}.{fieldRef.Name} (field returns {this.GetFriendlyTypeName(targetField.FieldType)}, not {this.GetFriendlyTypeName(fieldRef.FieldType)})");
+ return false;
}
}
@@ -75,21 +61,21 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
// get potential targets
MethodDefinition[] candidateMethods = methodReference.DeclaringType.Resolve()?.Methods.Where(found => found.Name == methodReference.Name).ToArray();
if (candidateMethods == null || !candidateMethods.Any())
- return InstructionHandleResult.None;
+ return false;
// compare return types
MethodDefinition methodDef = methodReference.Resolve();
if (methodDef == null)
- return InstructionHandleResult.None; // validated by ReferenceToMissingMemberFinder
+ return false; // validated by ReferenceToMissingMemberFinder
if (candidateMethods.All(method => !RewriteHelper.LooksLikeSameType(method.ReturnType, methodDef.ReturnType)))
{
- this.NounPhrase = $"reference to {methodDef.DeclaringType.FullName}.{methodDef.Name} (no such method returns {this.GetFriendlyTypeName(methodDef.ReturnType)})";
- return InstructionHandleResult.NotCompatible;
+ this.MarkFlag(InstructionHandleResult.NotCompatible, $"reference to {methodDef.DeclaringType.FullName}.{methodDef.Name} (no such method returns {this.GetFriendlyTypeName(methodDef.ReturnType)})");
+ return false;
}
}
- return InstructionHandleResult.None;
+ return false;
}
diff --git a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs
index 44b531a5..ebb62948 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs
@@ -1,13 +1,15 @@
+using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
+using StardewModdingAPI.Framework.ModLoading.Framework;
namespace StardewModdingAPI.Framework.ModLoading.Finders
{
/// <summary>Finds references to a field, property, or method which no longer exists.</summary>
/// <remarks>This implementation is purely heuristic. It should never return a false positive, but won't detect all cases.</remarks>
- internal class ReferenceToMissingMemberFinder : IInstructionHandler
+ internal class ReferenceToMissingMemberFinder : BaseInstructionHandler
{
/*********
** Fields
@@ -17,39 +19,23 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/*********
- ** Accessors
- *********/
- /// <summary>A brief noun phrase indicating what the instruction finder matches.</summary>
- public string NounPhrase { get; private set; } = "";
-
-
- /*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="validateReferencesToAssemblies">The assembly names to which to heuristically detect broken references.</param>
public ReferenceToMissingMemberFinder(string[] validateReferencesToAssemblies)
+ : base(defaultPhrase: "")
{
this.ValidateReferencesToAssemblies = new HashSet<string>(validateReferencesToAssemblies);
}
- /// <summary>Perform the predefined logic for a method if applicable.</summary>
- /// <param name="module">The assembly module containing the instruction.</param>
- /// <param name="method">The method definition containing the instruction.</param>
- /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
- /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
- {
- return InstructionHandleResult.None;
- }
-
- /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
+ /// <summary>Rewrite a CIL instruction reference if needed.</summary>
/// <param name="module">The assembly module containing the instruction.</param>
/// <param name="cil">The CIL processor.</param>
- /// <param name="instruction">The instruction to handle.</param>
- /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
- /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
+ /// <param name="instruction">The CIL instruction to handle.</param>
+ /// <param name="replaceWith">Replaces the CIL instruction with a new one.</param>
+ /// <returns>Returns whether the instruction was changed.</returns>
+ public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, Action<Instruction> replaceWith)
{
// field reference
FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
@@ -58,8 +44,8 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
FieldDefinition target = fieldRef.DeclaringType.Resolve()?.Fields.FirstOrDefault(p => p.Name == fieldRef.Name);
if (target == null)
{
- this.NounPhrase = $"reference to {fieldRef.DeclaringType.FullName}.{fieldRef.Name} (no such field)";
- return InstructionHandleResult.NotCompatible;
+ this.MarkFlag(InstructionHandleResult.NotCompatible, $"reference to {fieldRef.DeclaringType.FullName}.{fieldRef.Name} (no such field)");
+ return false;
}
}
@@ -70,17 +56,20 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
MethodDefinition target = methodRef.Resolve();
if (target == null)
{
+ string phrase = null;
if (this.IsProperty(methodRef))
- this.NounPhrase = $"reference to {methodRef.DeclaringType.FullName}.{methodRef.Name.Substring(4)} (no such property)";
+ phrase = $"reference to {methodRef.DeclaringType.FullName}.{methodRef.Name.Substring(4)} (no such property)";
else if (methodRef.Name == ".ctor")
- this.NounPhrase = $"reference to {methodRef.DeclaringType.FullName}.{methodRef.Name} (no matching constructor)";
+ phrase = $"reference to {methodRef.DeclaringType.FullName}.{methodRef.Name} (no matching constructor)";
else
- this.NounPhrase = $"reference to {methodRef.DeclaringType.FullName}.{methodRef.Name} (no such method)";
- return InstructionHandleResult.NotCompatible;
+ phrase = $"reference to {methodRef.DeclaringType.FullName}.{methodRef.Name} (no such method)";
+
+ this.MarkFlag(InstructionHandleResult.NotCompatible, phrase);
+ return false;
}
}
- return InstructionHandleResult.None;
+ return false;
}
diff --git a/src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs
new file mode 100644
index 00000000..a1ade536
--- /dev/null
+++ b/src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs
@@ -0,0 +1,51 @@
+using System;
+using Mono.Cecil;
+using StardewModdingAPI.Framework.ModLoading.Framework;
+
+namespace StardewModdingAPI.Framework.ModLoading.Finders
+{
+ /// <summary>Finds incompatible CIL instructions that reference types in a given assembly.</summary>
+ internal class TypeAssemblyFinder : BaseInstructionHandler
+ {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>The full assembly name to which to find references.</summary>
+ private readonly string AssemblyName;
+
+ /// <summary>The result to return for matching instructions.</summary>
+ private readonly InstructionHandleResult Result;
+
+ /// <summary>Get whether a matched type should be ignored.</summary>
+ private readonly Func<TypeReference, bool> ShouldIgnore;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="assemblyName">The full assembly name to which to find references.</param>
+ /// <param name="result">The result to return for matching instructions.</param>
+ /// <param name="shouldIgnore">Get whether a matched type should be ignored.</param>
+ public TypeAssemblyFinder(string assemblyName, InstructionHandleResult result, Func<TypeReference, bool> shouldIgnore = null)
+ : base(defaultPhrase: $"{assemblyName} assembly")
+ {
+ this.AssemblyName = assemblyName;
+ this.Result = result;
+ this.ShouldIgnore = shouldIgnore;
+ }
+
+ /// <summary>Rewrite a type reference if needed.</summary>
+ /// <param name="module">The assembly module containing the instruction.</param>
+ /// <param name="type">The type definition to handle.</param>
+ /// <param name="replaceWith">Replaces the type reference with a new one.</param>
+ /// <returns>Returns whether the type was changed.</returns>
+ public override bool Handle(ModuleDefinition module, TypeReference type, Action<TypeReference> replaceWith)
+ {
+ if (type.Scope.Name == this.AssemblyName && this.ShouldIgnore?.Invoke(type) != true)
+ this.MarkFlag(this.Result);
+
+ return false;
+ }
+ }
+}
diff --git a/src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs
index 701b15f2..c285414a 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs
@@ -1,31 +1,23 @@
using System;
-using System.Linq;
using Mono.Cecil;
-using Mono.Cecil.Cil;
+using StardewModdingAPI.Framework.ModLoading.Framework;
namespace StardewModdingAPI.Framework.ModLoading.Finders
{
/// <summary>Finds incompatible CIL instructions that reference a given type.</summary>
- internal class TypeFinder : IInstructionHandler
+ internal class TypeFinder : BaseInstructionHandler
{
/*********
- ** Accessors
+ ** Fields
*********/
- /// <summary>The full type name for which to find references.</summary>
+ /// <summary>The full type name to match.</summary>
private readonly string FullTypeName;
/// <summary>The result to return for matching instructions.</summary>
private readonly InstructionHandleResult Result;
- /// <summary>A lambda which overrides a matched type.</summary>
- protected readonly Func<TypeReference, bool> ShouldIgnore;
-
-
- /*********
- ** Accessors
- *********/
- /// <summary>A brief noun phrase indicating what the instruction finder matches.</summary>
- public string NounPhrase { get; }
+ /// <summary>Get whether a matched type should be ignored.</summary>
+ private readonly Func<TypeReference, bool> ShouldIgnore;
/*********
@@ -34,104 +26,24 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/// <summary>Construct an instance.</summary>
/// <param name="fullTypeName">The full type name to match.</param>
/// <param name="result">The result to return for matching instructions.</param>
- /// <param name="shouldIgnore">A lambda which overrides a matched type.</param>
+ /// <param name="shouldIgnore">Get whether a matched type should be ignored.</param>
public TypeFinder(string fullTypeName, InstructionHandleResult result, Func<TypeReference, bool> shouldIgnore = null)
+ : base(defaultPhrase: $"{fullTypeName} type")
{
this.FullTypeName = fullTypeName;
this.Result = result;
- this.NounPhrase = $"{fullTypeName} type";
- this.ShouldIgnore = shouldIgnore ?? (p => false);
- }
-
- /// <summary>Perform the predefined logic for a method if applicable.</summary>
- /// <param name="module">The assembly module containing the instruction.</param>
- /// <param name="method">The method definition containing the instruction.</param>
- /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
- /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
- {
- return this.IsMatch(method)
- ? this.Result
- : InstructionHandleResult.None;
+ this.ShouldIgnore = shouldIgnore;
}
- /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
+ /// <summary>Rewrite a type reference if needed.</summary>
/// <param name="module">The assembly module containing the instruction.</param>
- /// <param name="cil">The CIL processor.</param>
- /// <param name="instruction">The instruction to handle.</param>
- /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
- /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
- {
- return this.IsMatch(instruction)
- ? this.Result
- : InstructionHandleResult.None;
- }
-
-
- /*********
- ** Protected methods
- *********/
- /// <summary>Get whether a CIL instruction matches.</summary>
- /// <param name="method">The method definition.</param>
- protected bool IsMatch(MethodDefinition method)
+ /// <param name="type">The type definition to handle.</param>
+ /// <param name="replaceWith">Replaces the type reference with a new one.</param>
+ /// <returns>Returns whether the type was changed.</returns>
+ public override bool Handle(ModuleDefinition module, TypeReference type, Action<TypeReference> replaceWith)
{
- if (this.IsMatch(method.ReturnType))
- return true;
-
- foreach (VariableDefinition variable in method.Body.Variables)
- {
- if (this.IsMatch(variable.VariableType))
- return true;
- }
-
- return false;
- }
-
- /// <summary>Get whether a CIL instruction matches.</summary>
- /// <param name="instruction">The IL instruction.</param>
- protected bool IsMatch(Instruction instruction)
- {
- // field reference
- FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
- if (fieldRef != null)
- {
- return
- this.IsMatch(fieldRef.DeclaringType) // field on target class
- || this.IsMatch(fieldRef.FieldType); // field value is target class
- }
-
- // method reference
- MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);
- if (methodRef != null)
- {
- return
- this.IsMatch(methodRef.DeclaringType) // method on target class
- || this.IsMatch(methodRef.ReturnType) // method returns target class
- || methodRef.Parameters.Any(p => this.IsMatch(p.ParameterType)); // method parameters
- }
-
- return false;
- }
-
- /// <summary>Get whether a type reference matches the expected type.</summary>
- /// <param name="type">The type to check.</param>
- protected bool IsMatch(TypeReference type)
- {
- // root type
- if (type.FullName == this.FullTypeName && !this.ShouldIgnore(type))
- return true;
-
- // generic arguments
- if (type is GenericInstanceType genericType)
- {
- if (genericType.GenericArguments.Any(this.IsMatch))
- return true;
- }
-
- // generic parameters (e.g. constraints)
- if (type.GenericParameters.Any(this.IsMatch))
- return true;
+ if (type.FullName == this.FullTypeName && this.ShouldIgnore?.Invoke(type) != true)
+ this.MarkFlag(this.Result);
return false;
}