merge field rewriters to reduce rewrite iterations
This commit is contained in:
parent
1ea8d75235
commit
230f119205
|
@ -8,6 +8,7 @@
|
||||||
* Added automatic save recovery when the custom farm type isn't available anymore.
|
* Added automatic save recovery when the custom farm type isn't available anymore.
|
||||||
* Added the new game build number to the SMAPI console + log.
|
* Added the new game build number to the SMAPI console + log.
|
||||||
* The installer now detects Xbox app game folders.
|
* The installer now detects Xbox app game folders.
|
||||||
|
* Reduced mod loading time a bit.
|
||||||
* Fixed extra newlines shown in the console in non-developer mode.
|
* Fixed extra newlines shown in the console in non-developer mode.
|
||||||
* Fixed macOS launch issue when using some terminals (thanks to bruce2409!).
|
* Fixed macOS launch issue when using some terminals (thanks to bruce2409!).
|
||||||
* Fixed Linux/macOS terminal ignoring backspace in Stardew Valley 1.5.5+.
|
* Fixed Linux/macOS terminal ignoring backspace in Stardew Valley 1.5.5+.
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
|
||||||
** Fields
|
** Fields
|
||||||
*********/
|
*********/
|
||||||
/// <summary>The comparer which heuristically compares type definitions.</summary>
|
/// <summary>The comparer which heuristically compares type definitions.</summary>
|
||||||
private static readonly TypeReferenceComparer TypeDefinitionComparer = new TypeReferenceComparer();
|
private static readonly TypeReferenceComparer TypeDefinitionComparer = new();
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
|
@ -28,28 +28,6 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get whether the field is a reference to the expected type and field.</summary>
|
|
||||||
/// <param name="instruction">The IL instruction.</param>
|
|
||||||
/// <param name="fullTypeName">The full type name containing the expected field.</param>
|
|
||||||
/// <param name="fieldName">The name of the expected field.</param>
|
|
||||||
public static bool IsFieldReferenceTo(Instruction instruction, string fullTypeName, string fieldName)
|
|
||||||
{
|
|
||||||
FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
|
|
||||||
return RewriteHelper.IsFieldReferenceTo(fieldRef, fullTypeName, fieldName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Get whether the field is a reference to the expected type and field.</summary>
|
|
||||||
/// <param name="fieldRef">The field reference to check.</param>
|
|
||||||
/// <param name="fullTypeName">The full type name containing the expected field.</param>
|
|
||||||
/// <param name="fieldName">The name of the expected field.</param>
|
|
||||||
public static bool IsFieldReferenceTo(FieldReference fieldRef, string fullTypeName, string fieldName)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
fieldRef != null
|
|
||||||
&& fieldRef.DeclaringType.FullName == fullTypeName
|
|
||||||
&& fieldRef.Name == fieldName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Get the method reference from an instruction if it matches.</summary>
|
/// <summary>Get the method reference from an instruction if it matches.</summary>
|
||||||
/// <param name="instruction">The IL instruction.</param>
|
/// <param name="instruction">The IL instruction.</param>
|
||||||
public static MethodReference AsMethodReference(Instruction instruction)
|
public static MethodReference AsMethodReference(Instruction instruction)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
|
@ -12,54 +13,55 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
|
||||||
/*********
|
/*********
|
||||||
** Fields
|
** Fields
|
||||||
*********/
|
*********/
|
||||||
/// <summary>The type containing the field to which references should be rewritten.</summary>
|
/// <summary>The new fields to reference indexed by the old field/type names.</summary>
|
||||||
private readonly Type Type;
|
private readonly Dictionary<string, Dictionary<string, FieldInfo>> FieldMaps = new();
|
||||||
|
|
||||||
/// <summary>The field name to which references should be rewritten.</summary>
|
|
||||||
private readonly string FromFieldName;
|
|
||||||
|
|
||||||
/// <summary>The new field to reference.</summary>
|
|
||||||
private readonly FieldInfo ToField;
|
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Public methods
|
** Public methods
|
||||||
*********/
|
*********/
|
||||||
/// <summary>Construct an instance.</summary>
|
/// <summary>Construct an instance.</summary>
|
||||||
|
public FieldReplaceRewriter()
|
||||||
|
: base(defaultPhrase: "field replacement") { } // will be overridden when a field is replaced
|
||||||
|
|
||||||
|
/// <summary>Add a field to replace.</summary>
|
||||||
/// <param name="fromType">The type whose field to rewrite.</param>
|
/// <param name="fromType">The type whose field to rewrite.</param>
|
||||||
/// <param name="fromFieldName">The field name to rewrite.</param>
|
/// <param name="fromFieldName">The field name to rewrite.</param>
|
||||||
/// <param name="toType">The new type which will have the field.</param>
|
/// <param name="toType">The new type which will have the field.</param>
|
||||||
/// <param name="toFieldName">The new field name to reference.</param>
|
/// <param name="toFieldName">The new field name to reference.</param>
|
||||||
public FieldReplaceRewriter(Type fromType, string fromFieldName, Type toType, string toFieldName)
|
public FieldReplaceRewriter AddField(Type fromType, string fromFieldName, Type toType, string toFieldName)
|
||||||
: base(defaultPhrase: $"{fromType.FullName}.{fromFieldName} field")
|
|
||||||
{
|
{
|
||||||
this.Type = fromType;
|
// get full type name
|
||||||
this.FromFieldName = fromFieldName;
|
string fromTypeName = fromType?.FullName;
|
||||||
this.ToField = toType.GetField(toFieldName);
|
if (fromTypeName == null)
|
||||||
if (this.ToField == null)
|
throw new InvalidOperationException($"Can't replace field for invalid type reference {toType}.");
|
||||||
throw new InvalidOperationException($"The {toType.FullName} class doesn't have a {toFieldName} field.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Construct an instance.</summary>
|
// get target field
|
||||||
/// <param name="type">The type whose field to rewrite.</param>
|
FieldInfo toField = toType.GetField(toFieldName);
|
||||||
/// <param name="fromFieldName">The field name to rewrite.</param>
|
if (toField == null)
|
||||||
/// <param name="toFieldName">The new field name to reference.</param>
|
throw new InvalidOperationException($"The {toType.FullName} class doesn't have a {toFieldName} field.");
|
||||||
public FieldReplaceRewriter(Type type, string fromFieldName, string toFieldName)
|
|
||||||
: this(type, fromFieldName, type, toFieldName)
|
// add mapping
|
||||||
{
|
if (!this.FieldMaps.TryGetValue(fromTypeName, out var fieldMap))
|
||||||
|
this.FieldMaps[fromTypeName] = fieldMap = new();
|
||||||
|
fieldMap[fromFieldName] = toField;
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
|
public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
|
||||||
{
|
{
|
||||||
// get field reference
|
|
||||||
FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
|
FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
|
||||||
if (!RewriteHelper.IsFieldReferenceTo(fieldRef, this.Type.FullName, this.FromFieldName))
|
string declaringType = fieldRef?.DeclaringType?.FullName;
|
||||||
|
|
||||||
|
// get mapped field
|
||||||
|
if (declaringType == null || !this.FieldMaps.TryGetValue(declaringType, out var fieldMap) || !fieldMap.TryGetValue(fieldRef.Name, out FieldInfo toField))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// replace with new field
|
// replace with new field
|
||||||
instruction.Operand = module.ImportReference(this.ToField);
|
this.Phrases.Add($"{fieldRef.DeclaringType.Name}.{fieldRef.Name} field");
|
||||||
|
instruction.Operand = module.ImportReference(toField);
|
||||||
return this.MarkRewritten();
|
return this.MarkRewritten();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,9 +37,10 @@ namespace StardewModdingAPI.Metadata
|
||||||
if (rewriteMods)
|
if (rewriteMods)
|
||||||
{
|
{
|
||||||
// rewrite for Stardew Valley 1.5
|
// rewrite for Stardew Valley 1.5
|
||||||
yield return new FieldReplaceRewriter(typeof(DecoratableLocation), "furniture", typeof(GameLocation), nameof(GameLocation.furniture));
|
yield return new FieldReplaceRewriter()
|
||||||
yield return new FieldReplaceRewriter(typeof(Farm), "resourceClumps", typeof(GameLocation), nameof(GameLocation.resourceClumps));
|
.AddField(typeof(DecoratableLocation), "furniture", typeof(GameLocation), nameof(GameLocation.furniture))
|
||||||
yield return new FieldReplaceRewriter(typeof(MineShaft), "resourceClumps", typeof(GameLocation), nameof(GameLocation.resourceClumps));
|
.AddField(typeof(Farm), "resourceClumps", typeof(GameLocation), nameof(GameLocation.resourceClumps))
|
||||||
|
.AddField(typeof(MineShaft), "resourceClumps", typeof(GameLocation), nameof(GameLocation.resourceClumps));
|
||||||
|
|
||||||
// heuristic rewrites
|
// heuristic rewrites
|
||||||
yield return new HeuristicFieldRewriter(this.ValidateReferencesToAssemblies);
|
yield return new HeuristicFieldRewriter(this.ValidateReferencesToAssemblies);
|
||||||
|
@ -87,7 +88,7 @@ namespace StardewModdingAPI.Metadata
|
||||||
typeof(System.IO.DirectoryInfo).FullName,
|
typeof(System.IO.DirectoryInfo).FullName,
|
||||||
typeof(System.IO.DriveInfo).FullName,
|
typeof(System.IO.DriveInfo).FullName,
|
||||||
typeof(System.IO.FileSystemWatcher).FullName
|
typeof(System.IO.FileSystemWatcher).FullName
|
||||||
},
|
},
|
||||||
InstructionHandleResult.DetectedFilesystemAccess
|
InstructionHandleResult.DetectedFilesystemAccess
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue