diff options
3 files changed, 54 insertions, 6 deletions
diff --git a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs index 0e7344a8..b5e45742 100644 --- a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs +++ b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using Mono.Cecil; @@ -12,6 +13,9 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders /********* ** Properties *********/ + /// <summary>The assembly names to which to heuristically detect broken references.</summary> + private readonly HashSet<string> ValidateReferencesToAssemblies; + /// <summary>A pattern matching type name substrings to strip for display.</summary> private readonly Regex StripTypeNamePattern = new Regex(@"`\d+(?=<)", RegexOptions.Compiled); @@ -26,6 +30,13 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders /********* ** 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) + { + 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> @@ -46,7 +57,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders { // field reference FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction); - if (fieldRef != null) + if (fieldRef != null && this.ShouldValidate(fieldRef.DeclaringType)) { // can't compare generic type parameters between definition and reference if (fieldRef.FieldType.IsGenericInstance || fieldRef.FieldType.IsGenericParameter) @@ -69,7 +80,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders // method reference MethodReference methodReference = RewriteHelper.AsMethodReference(instruction); - if (methodReference != null) + if (methodReference != null && this.ShouldValidate(methodReference.DeclaringType)) { // can't compare generic type parameters between definition and reference if (methodReference.ReturnType.IsGenericInstance || methodReference.ReturnType.IsGenericParameter) @@ -103,6 +114,13 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders /********* ** Private methods *********/ + /// <summary>Whether references to the given type should be validated.</summary> + /// <param name="type">The type reference.</param> + private bool ShouldValidate(TypeReference type) + { + return type != null && this.ValidateReferencesToAssemblies.Contains(type.Scope.Name); + } + /// <summary>Get a unique string representation of a type.</summary> /// <param name="type">The type reference.</param> private string GetComparableTypeID(TypeReference type) diff --git a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs index 60373c9d..f5e33313 100644 --- a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs +++ b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using Mono.Cecil; using Mono.Cecil.Cil; @@ -9,6 +10,13 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders internal class ReferenceToMissingMemberFinder : IInstructionHandler { /********* + ** Properties + *********/ + /// <summary>The assembly names to which to heuristically detect broken references.</summary> + private readonly HashSet<string> ValidateReferencesToAssemblies; + + + /********* ** Accessors *********/ /// <summary>A brief noun phrase indicating what the instruction finder matches.</summary> @@ -18,6 +26,13 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders /********* ** 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) + { + 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> @@ -38,7 +53,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders { // field reference FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction); - if (fieldRef != null) + if (fieldRef != null && this.ShouldValidate(fieldRef.DeclaringType)) { FieldDefinition target = fieldRef.DeclaringType.Resolve()?.Fields.FirstOrDefault(p => p.Name == fieldRef.Name); if (target == null) @@ -50,7 +65,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders // method reference MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); - if (methodRef != null && !this.IsUnsupported(methodRef)) + if (methodRef != null && this.ShouldValidate(methodRef.DeclaringType) && !this.IsUnsupported(methodRef)) { MethodDefinition target = methodRef.DeclaringType.Resolve()?.Methods.FirstOrDefault(p => p.Name == methodRef.Name); if (target == null) @@ -69,6 +84,13 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders /********* ** Private methods *********/ + /// <summary>Whether references to the given type should be validated.</summary> + /// <param name="type">The type reference.</param> + private bool ShouldValidate(TypeReference type) + { + return type != null && this.ValidateReferencesToAssemblies.Contains(type.Scope.Name); + } + /// <summary>Get whether a method reference is a special case that's not currently supported (e.g. array methods).</summary> /// <param name="method">The method reference.</param> private bool IsUnsupported(MethodReference method) diff --git a/src/SMAPI/Metadata/InstructionMetadata.cs b/src/SMAPI/Metadata/InstructionMetadata.cs index 9e2b7967..4ed1e38e 100644 --- a/src/SMAPI/Metadata/InstructionMetadata.cs +++ b/src/SMAPI/Metadata/InstructionMetadata.cs @@ -13,6 +13,14 @@ namespace StardewModdingAPI.Metadata internal class InstructionMetadata { /********* + ** Properties + *********/ + /// <summary>The assembly names to which to heuristically detect broken references.</summary> + /// <remarks>The current implementation only works correctly with assemblies that should always be present.</remarks> + private readonly string[] ValidateReferencesToAssemblies = { "StardewModdingAPI", "Stardew Valley", "StardewValley" }; + + + /********* ** Public methods *********/ /// <summary>Get rewriters which detect or fix incompatible CIL instructions in mod assemblies.</summary> @@ -87,8 +95,8 @@ namespace StardewModdingAPI.Metadata new PropertyFinder("StardewModdingAPI.Mod", "PerSaveConfigPath", InstructionHandleResult.NotCompatible), // broken code - new ReferenceToMissingMemberFinder(), - new ReferenceToMemberWithUnexpectedTypeFinder(), + new ReferenceToMissingMemberFinder(this.ValidateReferencesToAssemblies), + new ReferenceToMemberWithUnexpectedTypeFinder(this.ValidateReferencesToAssemblies), /**** ** detect code which may impact game stability |