encapsulate GetComparableTypeID

This commit is contained in:
Jesse Plamondon-Willard 2018-07-08 13:56:46 -04:00
parent 40fbafdb73
commit befeafd31d
2 changed files with 39 additions and 34 deletions

View File

@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Mono.Cecil;
using Mono.Cecil.Cil;
@ -16,9 +15,6 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/// <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);
/*********
** Accessors
@ -65,12 +61,9 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
return InstructionHandleResult.None;
// validate return type
string actualReturnTypeID = this.GetComparableTypeID(targetField.FieldType);
string expectedReturnTypeID = this.GetComparableTypeID(fieldRef.FieldType);
if (!RewriteHelper.LooksLikeSameType(expectedReturnTypeID, actualReturnTypeID))
if (!RewriteHelper.LooksLikeSameType(fieldRef.FieldType, targetField.FieldType))
{
this.NounPhrase = $"reference to {fieldRef.DeclaringType.FullName}.{fieldRef.Name} (field returns {this.GetFriendlyTypeName(targetField.FieldType, actualReturnTypeID)}, not {this.GetFriendlyTypeName(fieldRef.FieldType, expectedReturnTypeID)})";
this.NounPhrase = $"reference to {fieldRef.DeclaringType.FullName}.{fieldRef.Name} (field returns {this.GetFriendlyTypeName(targetField.FieldType)}, not {this.GetFriendlyTypeName(fieldRef.FieldType)})";
return InstructionHandleResult.NotCompatible;
}
}
@ -92,10 +85,9 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
return InstructionHandleResult.NotCompatible;
}
string expectedReturnType = this.GetComparableTypeID(methodDef.ReturnType);
if (candidateMethods.All(method => !RewriteHelper.LooksLikeSameType(this.GetComparableTypeID(method.ReturnType), expectedReturnType)))
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, expectedReturnType)})";
this.NounPhrase = $"reference to {methodDef.DeclaringType.FullName}.{methodDef.Name} (no such method returns {this.GetFriendlyTypeName(methodDef.ReturnType)})";
return InstructionHandleResult.NotCompatible;
}
}
@ -114,17 +106,9 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
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)
{
return this.StripTypeNamePattern.Replace(type.FullName, "");
}
/// <summary>Get a shorter type name for display.</summary>
/// <param name="type">The type reference.</param>
/// <param name="typeID">The comparable type ID from <see cref="GetComparableTypeID"/>.</param>
private string GetFriendlyTypeName(TypeReference type, string typeID)
private string GetFriendlyTypeName(TypeReference type)
{
// most common built-in types
switch (type.FullName)
@ -141,10 +125,10 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
foreach (string @namespace in new[] { "Microsoft.Xna.Framework", "Netcode", "System", "System.Collections.Generic" })
{
if (type.Namespace == @namespace)
return typeID.Substring(@namespace.Length + 1);
return type.Name;
}
return typeID;
return type.FullName;
}
}
}

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Mono.Cecil;
using Mono.Cecil.Cil;
@ -10,6 +11,13 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <summary>Provides helper methods for field rewriters.</summary>
internal static class RewriteHelper
{
/*********
** Properties
*********/
/// <summary>A pattern matching type name substrings to strip for display.</summary>
private static readonly Regex StripTypeNamePattern = new Regex(@"`\d+(?=<)", RegexOptions.Compiled);
/*********
** Public methods
*********/
@ -109,29 +117,39 @@ namespace StardewModdingAPI.Framework.ModLoading
}
/// <summary>Determine whether two type IDs look like the same type, accounting for placeholder values such as !0.</summary>
/// <param name="typeA">The type ID to compare.</param>
/// <param name="typeB">The other type ID to compare.</param>
/// <param name="a">The type ID to compare.</param>
/// <param name="b">The other type ID to compare.</param>
/// <returns>true if the type IDs look like the same type, false if not.</returns>
public static bool LooksLikeSameType(string typeA, string typeB)
public static bool LooksLikeSameType(TypeReference a, TypeReference b)
{
string typeA = RewriteHelper.GetComparableTypeID(a);
string typeB = RewriteHelper.GetComparableTypeID(b);
string placeholderType = "", actualType = "";
if (RewriteHelper.HasPlaceholder(typeA))
{
placeholderType = typeA;
actualType = typeB;
} else if (RewriteHelper.HasPlaceholder(typeB))
}
else if (RewriteHelper.HasPlaceholder(typeB))
{
placeholderType = typeB;
actualType = typeA;
} else
{
return typeA == typeB;
}
else
return typeA == typeB;
return RewriteHelper.PlaceholderTypeValidates(placeholderType, actualType);
}
/// <summary>Get a unique string representation of a type.</summary>
/// <param name="type">The type reference.</param>
private static string GetComparableTypeID(TypeReference type)
{
return RewriteHelper.StripTypeNamePattern.Replace(type.FullName, "");
}
protected class SymbolLocation
{
public string symbol;
@ -144,7 +162,7 @@ namespace StardewModdingAPI.Framework.ModLoading
}
}
private static List<char> symbolBoundaries = new List<char>{'<', '>', ','};
private static List<char> symbolBoundaries = new List<char> { '<', '>', ',' };
/// <summary> Traverses and parses out symbols from a type which does not contain placeholder values.</summary>
/// <param name="type">The type to traverse.</param>
@ -160,7 +178,8 @@ namespace StardewModdingAPI.Framework.ModLoading
{
typeSymbols.Add(new SymbolLocation(symbol, depth));
symbol = "";
switch (c) {
switch (c)
{
case '<':
depth++;
break;
@ -186,7 +205,8 @@ namespace StardewModdingAPI.Framework.ModLoading
if (symbolA.depth != symbolB.depth)
return false;
if (!RewriteHelper.IsPlaceholder(symbolA.symbol)) {
if (!RewriteHelper.IsPlaceholder(symbolA.symbol))
{
return symbolA.symbol == symbolB.symbol;
}
@ -245,7 +265,8 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <param name="placeholderType">The type with placeholders in it.</param>
/// <param name="actualType">The type without placeholders.</param>
/// <returns>true if the placeholder type can resolve to the actual type, false if not.</returns>
private static bool PlaceholderTypeValidates(string placeholderType, string actualType) {
private static bool PlaceholderTypeValidates(string placeholderType, string actualType)
{
List<SymbolLocation> typeSymbols = new List<SymbolLocation>();
RewriteHelper.TraverseActualType(actualType, typeSymbols);