summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework/ModLoading
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2020-09-07 13:06:27 -0400
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2020-09-07 13:06:27 -0400
commit5e43bdbf5cd6dbab36c25287c85d42ccfeea2c83 (patch)
tree0a42305174eb84561a584549cd685c5e95670f36 /src/SMAPI/Framework/ModLoading
parent8da88b8fe5b41739c5cd0df3280b9770fc7f10a4 (diff)
parentf9fac11028354f15d786d5b854608edb10716f79 (diff)
downloadSMAPI-5e43bdbf5cd6dbab36c25287c85d42ccfeea2c83.tar.gz
SMAPI-5e43bdbf5cd6dbab36c25287c85d42ccfeea2c83.tar.bz2
SMAPI-5e43bdbf5cd6dbab36c25287c85d42ccfeea2c83.zip
Merge branch 'develop' into stable
Diffstat (limited to 'src/SMAPI/Framework/ModLoading')
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyLoader.cs31
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs10
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs10
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs10
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs10
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs10
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs17
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs6
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs6
-rw-r--r--src/SMAPI/Framework/ModLoading/Framework/BaseInstructionHandler.cs23
-rw-r--r--src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs67
-rw-r--r--src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs24
-rw-r--r--src/SMAPI/Framework/ModLoading/IInstructionHandler.cs3
-rw-r--r--src/SMAPI/Framework/ModLoading/ModFailReason.cs27
-rw-r--r--src/SMAPI/Framework/ModLoading/ModMetadata.cs95
-rw-r--r--src/SMAPI/Framework/ModLoading/ModResolver.cs48
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/FieldReplaceRewriter.cs35
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/FieldToPropertyRewriter.cs65
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/Harmony1AssemblyRewriter.cs13
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs106
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/HeuristicMethodRewriter.cs109
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs9
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/StaticFieldToConstantRewriter.cs74
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs6
24 files changed, 418 insertions, 396 deletions
diff --git a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
index f8c901e0..9fb5384e 100644
--- a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
+++ b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
@@ -76,10 +76,9 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <param name="mod">The mod for which the assembly is being loaded.</param>
/// <param name="assemblyPath">The assembly file path.</param>
/// <param name="assumeCompatible">Assume the mod is compatible, even if incompatible code is detected.</param>
- /// <param name="rewriteInParallel">Whether to enable experimental parallel rewriting.</param>
/// <returns>Returns the rewrite metadata for the preprocessed assembly.</returns>
/// <exception cref="IncompatibleInstructionException">An incompatible CIL instruction was found while rewriting the assembly.</exception>
- public Assembly Load(IModMetadata mod, string assemblyPath, bool assumeCompatible, bool rewriteInParallel)
+ public Assembly Load(IModMetadata mod, string assemblyPath, bool assumeCompatible)
{
// get referenced local assemblies
AssemblyParseResult[] assemblies;
@@ -109,7 +108,7 @@ namespace StardewModdingAPI.Framework.ModLoading
continue;
// rewrite assembly
- bool changed = this.RewriteAssembly(mod, assembly.Definition, loggedMessages, logPrefix: " ", rewriteInParallel);
+ bool changed = this.RewriteAssembly(mod, assembly.Definition, loggedMessages, logPrefix: " ");
// detect broken assembly reference
foreach (AssemblyNameReference reference in assembly.Definition.MainModule.AssemblyReferences)
@@ -263,10 +262,9 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <param name="assembly">The assembly to rewrite.</param>
/// <param name="loggedMessages">The messages that have already been logged for this mod.</param>
/// <param name="logPrefix">A string to prefix to log messages.</param>
- /// <param name="rewriteInParallel">Whether to enable experimental parallel rewriting.</param>
/// <returns>Returns whether the assembly was modified.</returns>
/// <exception cref="IncompatibleInstructionException">An incompatible CIL instruction was found while rewriting the assembly.</exception>
- private bool RewriteAssembly(IModMetadata mod, AssemblyDefinition assembly, HashSet<string> loggedMessages, string logPrefix, bool rewriteInParallel)
+ private bool RewriteAssembly(IModMetadata mod, AssemblyDefinition assembly, HashSet<string> loggedMessages, string logPrefix)
{
ModuleDefinition module = assembly.MainModule;
string filename = $"{assembly.Name.Name}.dll";
@@ -294,6 +292,19 @@ namespace StardewModdingAPI.Framework.ModLoading
IEnumerable<TypeReference> typeReferences = module.GetTypeReferences().OrderBy(p => p.FullName);
foreach (TypeReference type in typeReferences)
this.ChangeTypeScope(type);
+
+ // rewrite types using custom attributes
+ foreach (TypeDefinition type in module.GetTypes())
+ {
+ foreach (var attr in type.CustomAttributes)
+ {
+ foreach (var conField in attr.ConstructorArguments)
+ {
+ if (conField.Value is TypeReference typeRef)
+ this.ChangeTypeScope(typeRef);
+ }
+ }
+ }
}
// find or rewrite code
@@ -307,15 +318,15 @@ namespace StardewModdingAPI.Framework.ModLoading
rewritten |= handler.Handle(module, type, replaceWith);
return rewritten;
},
- rewriteInstruction: (ref Instruction instruction, ILProcessor cil, Action<Instruction> replaceWith) =>
+ rewriteInstruction: (ref Instruction instruction, ILProcessor cil) =>
{
bool rewritten = false;
foreach (IInstructionHandler handler in handlers)
- rewritten |= handler.Handle(module, cil, instruction, replaceWith);
+ rewritten |= handler.Handle(module, cil, instruction);
return rewritten;
}
);
- bool anyRewritten = rewriter.RewriteModule(rewriteInParallel);
+ bool anyRewritten = rewriter.RewriteModule();
// handle rewrite flags
foreach (IInstructionHandler handler in handlers)
@@ -398,10 +409,10 @@ namespace StardewModdingAPI.Framework.ModLoading
if (handler.Phrases.Any())
{
foreach (string message in handler.Phrases)
- this.Monitor.LogOnce(template.Replace("$phrase", message));
+ this.Monitor.LogOnce(loggedMessages, template.Replace("$phrase", message));
}
else
- this.Monitor.LogOnce(template.Replace("$phrase", handler.DefaultPhrase ?? handler.GetType().Name));
+ this.Monitor.LogOnce(loggedMessages, template.Replace("$phrase", handler.DefaultPhrase ?? handler.GetType().Name));
}
/// <summary>Get the correct reference to use for compatibility with the current platform.</summary>
diff --git a/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs
index e1476b73..01ed153b 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs
@@ -1,4 +1,3 @@
-using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
using StardewModdingAPI.Framework.ModLoading.Framework;
@@ -36,13 +35,8 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
this.Result = result;
}
- /// <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 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)
+ /// <inheritdoc />
+ public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
{
if (!this.Flags.Contains(this.Result) && this.IsMatch(instruction))
this.MarkFlag(this.Result);
diff --git a/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs
index c157ed9b..2c062243 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs
@@ -1,4 +1,3 @@
-using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
using StardewModdingAPI.Framework.ModLoading.Framework;
@@ -36,13 +35,8 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
this.Result = result;
}
- /// <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 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)
+ /// <inheritdoc />
+ public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
{
if (!this.Flags.Contains(this.Result) && RewriteHelper.IsFieldReferenceTo(instruction, this.FullTypeName, this.FieldName))
this.MarkFlag(this.Result);
diff --git a/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs
index 82c93a7c..d2340f01 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs
@@ -1,4 +1,3 @@
-using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
using StardewModdingAPI.Framework.ModLoading.Framework;
@@ -36,13 +35,8 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
this.Result = result;
}
- /// <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 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)
+ /// <inheritdoc />
+ public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
{
if (!this.Flags.Contains(this.Result) && this.IsMatch(instruction))
this.MarkFlag(this.Result);
diff --git a/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs
index c96d61a2..99344848 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs
@@ -1,4 +1,3 @@
-using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
using StardewModdingAPI.Framework.ModLoading.Framework;
@@ -36,13 +35,8 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
this.Result = result;
}
- /// <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 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)
+ /// <inheritdoc />
+ public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
{
if (!this.Flags.Contains(this.Result) && this.IsMatch(instruction))
this.MarkFlag(this.Result);
diff --git a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs
index a67cfa4f..b01a3240 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
@@ -29,13 +28,8 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
this.ValidateReferencesToAssemblies = new HashSet<string>(validateReferencesToAssemblies);
}
- /// <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 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)
+ /// <inheritdoc />
+ public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
{
// field reference
FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
diff --git a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs
index ebb62948..b64a255e 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs
@@ -1,6 +1,4 @@
-using System;
using System.Collections.Generic;
-using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
using StardewModdingAPI.Framework.ModLoading.Framework;
@@ -29,20 +27,15 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
this.ValidateReferencesToAssemblies = new HashSet<string>(validateReferencesToAssemblies);
}
- /// <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 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)
+ /// <inheritdoc />
+ public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
{
// field reference
FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
if (fieldRef != null && this.ShouldValidate(fieldRef.DeclaringType))
{
- FieldDefinition target = fieldRef.DeclaringType.Resolve()?.Fields.FirstOrDefault(p => p.Name == fieldRef.Name);
- if (target == null)
+ FieldDefinition target = fieldRef.Resolve();
+ if (target == null || target.HasConstant)
{
this.MarkFlag(InstructionHandleResult.NotCompatible, $"reference to {fieldRef.DeclaringType.FullName}.{fieldRef.Name} (no such field)");
return false;
@@ -56,7 +49,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
MethodDefinition target = methodRef.Resolve();
if (target == null)
{
- string phrase = null;
+ string phrase;
if (this.IsProperty(methodRef))
phrase = $"reference to {methodRef.DeclaringType.FullName}.{methodRef.Name.Substring(4)} (no such property)";
else if (methodRef.Name == ".ctor")
diff --git a/src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs
index a1ade536..24ab2eca 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs
@@ -35,11 +35,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
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>
+ /// <inheritdoc />
public override bool Handle(ModuleDefinition module, TypeReference type, Action<TypeReference> replaceWith)
{
if (type.Scope.Name == this.AssemblyName && this.ShouldIgnore?.Invoke(type) != true)
diff --git a/src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs
index c285414a..bbd081e8 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs
@@ -35,11 +35,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
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>
+ /// <inheritdoc />
public override bool Handle(ModuleDefinition module, TypeReference type, Action<TypeReference> replaceWith)
{
if (type.FullName == this.FullTypeName && this.ShouldIgnore?.Invoke(type) != true)
diff --git a/src/SMAPI/Framework/ModLoading/Framework/BaseInstructionHandler.cs b/src/SMAPI/Framework/ModLoading/Framework/BaseInstructionHandler.cs
index fde37d68..624113b3 100644
--- a/src/SMAPI/Framework/ModLoading/Framework/BaseInstructionHandler.cs
+++ b/src/SMAPI/Framework/ModLoading/Framework/BaseInstructionHandler.cs
@@ -11,36 +11,27 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
/*********
** Accessors
*********/
- /// <summary>A brief noun phrase indicating what the handler matches, used if <see cref="Phrases"/> is empty.</summary>
+ /// <inheritdoc />
public string DefaultPhrase { get; }
- /// <summary>The rewrite flags raised for the current module.</summary>
+ /// <inheritdoc />
public ISet<InstructionHandleResult> Flags { get; } = new HashSet<InstructionHandleResult>();
- /// <summary>The brief noun phrases indicating what the handler matched for the current module.</summary>
+ /// <inheritdoc />
public ISet<string> Phrases { get; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
/*********
** Public methods
*********/
- /// <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>
+ /// <inheritdoc />
public virtual bool Handle(ModuleDefinition module, TypeReference type, Action<TypeReference> replaceWith)
{
return false;
}
- /// <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 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 virtual bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, Action<Instruction> replaceWith)
+ /// <inheritdoc />
+ public virtual bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
{
return false;
}
@@ -50,7 +41,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
** Protected methods
*********/
/// <summary>Construct an instance.</summary>
- /// <param name="defaultPhrase">A brief noun phrase indicating what the handler matches.</param>
+ /// <param name="defaultPhrase">A brief noun phrase indicating what the handler matches, used if <see cref="Phrases"/> is empty.</param>
protected BaseInstructionHandler(string defaultPhrase)
{
this.DefaultPhrase = defaultPhrase;
diff --git a/src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs b/src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs
index 34c78c7d..ea29550a 100644
--- a/src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
@@ -24,9 +22,8 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
/// <summary>Rewrite a CIL instruction in the assembly code.</summary>
/// <param name="instruction">The current CIL instruction.</param>
/// <param name="cil">The CIL instruction processor.</param>
- /// <param name="replaceWith">Replaces the CIL instruction with the given instruction.</param>
/// <returns>Returns whether the instruction was changed.</returns>
- public delegate bool RewriteInstructionDelegate(ref Instruction instruction, ILProcessor cil, Action<Instruction> replaceWith);
+ public delegate bool RewriteInstructionDelegate(ref Instruction instruction, ILProcessor cil);
/*********
@@ -57,59 +54,24 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
}
/// <summary>Rewrite the loaded module code.</summary>
- /// <param name="rewriteInParallel">Whether to enable experimental parallel rewriting.</param>
/// <returns>Returns whether the module was modified.</returns>
- public bool RewriteModule(bool rewriteInParallel)
+ public bool RewriteModule()
{
IEnumerable<TypeDefinition> types = this.Module.GetTypes().Where(type => type.BaseType != null); // skip special types like <Module>
- // experimental parallel rewriting
- // This may cause intermittent startup errors and is disabled by default: https://github.com/Pathoschild/SMAPI/issues/721
- if (rewriteInParallel)
- {
- int typesChanged = 0;
- Exception exception = null;
-
- Parallel.ForEach(types, type =>
- {
- if (exception != null)
- return;
-
- bool changed = false;
- try
- {
- changed = this.RewriteTypeDefinition(type);
- }
- catch (Exception ex)
- {
- exception ??= ex;
- }
-
- if (changed)
- Interlocked.Increment(ref typesChanged);
- });
+ bool changed = false;
- return exception == null
- ? typesChanged > 0
- : throw new Exception($"Rewriting {this.Module.Name} failed.", exception);
+ try
+ {
+ foreach (var type in types)
+ changed |= this.RewriteTypeDefinition(type);
}
-
- // non-parallel rewriting
+ catch (Exception ex)
{
- bool changed = false;
-
- try
- {
- foreach (var type in types)
- changed |= this.RewriteTypeDefinition(type);
- }
- catch (Exception ex)
- {
- throw new Exception($"Rewriting {this.Module.Name} failed.", ex);
- }
-
- return changed;
+ throw new Exception($"Rewriting {this.Module.Name} failed.", ex);
}
+
+ return changed;
}
@@ -198,12 +160,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
// instruction itself
// (should be done after the above type rewrites to ensure valid types)
- rewritten |= this.RewriteInstructionImpl(ref instruction, cil, newInstruction =>
- {
- rewritten = true;
- cil.Replace(instruction, newInstruction);
- instruction = newInstruction;
- });
+ rewritten |= this.RewriteInstructionImpl(ref instruction, cil);
return rewritten;
}
diff --git a/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs b/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs
index 36058b86..207b6445 100644
--- a/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs
+++ b/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs
@@ -59,12 +59,30 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
: null;
}
+ /// <summary>Get the CIL instruction to load a value onto the stack.</summary>
+ /// <param name="rawValue">The constant value to inject.</param>
+ /// <returns>Returns the instruction, or <c>null</c> if the value type isn't supported.</returns>
+ public static Instruction GetLoadValueInstruction(object rawValue)
+ {
+ return rawValue switch
+ {
+ null => Instruction.Create(OpCodes.Ldnull),
+ bool value => Instruction.Create(value ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0),
+ int value => Instruction.Create(OpCodes.Ldc_I4, value), // int32
+ long value => Instruction.Create(OpCodes.Ldc_I8, value), // int64
+ float value => Instruction.Create(OpCodes.Ldc_R4, value), // float32
+ double value => Instruction.Create(OpCodes.Ldc_R8, value), // float64
+ string value => Instruction.Create(OpCodes.Ldstr, value),
+ _ => null
+ };
+ }
+
/// <summary>Get whether a type matches a type reference.</summary>
/// <param name="type">The defined type.</param>
/// <param name="reference">The type reference.</param>
public static bool IsSameType(Type type, TypeReference reference)
{
- //
+ //
// duplicated by IsSameType(TypeReference, TypeReference) below
//
@@ -139,7 +157,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
/// <param name="reference">The method reference.</param>
public static bool HasMatchingSignature(MethodBase definition, MethodReference reference)
{
- //
+ //
// duplicated by HasMatchingSignature(MethodDefinition, MethodReference) below
//
@@ -165,7 +183,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
/// <param name="reference">The method reference.</param>
public static bool HasMatchingSignature(MethodDefinition definition, MethodReference reference)
{
- //
+ //
// duplicated by HasMatchingSignature(MethodBase, MethodReference) above
//
diff --git a/src/SMAPI/Framework/ModLoading/IInstructionHandler.cs b/src/SMAPI/Framework/ModLoading/IInstructionHandler.cs
index e6de6785..17c9ba68 100644
--- a/src/SMAPI/Framework/ModLoading/IInstructionHandler.cs
+++ b/src/SMAPI/Framework/ModLoading/IInstructionHandler.cs
@@ -35,8 +35,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <param name="module">The assembly module containing the instruction.</param>
/// <param name="cil">The CIL processor.</param>
/// <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>
- bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, Action<Instruction> replaceWith);
+ bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction);
}
}
diff --git a/src/SMAPI/Framework/ModLoading/ModFailReason.cs b/src/SMAPI/Framework/ModLoading/ModFailReason.cs
new file mode 100644
index 00000000..cd4623e7
--- /dev/null
+++ b/src/SMAPI/Framework/ModLoading/ModFailReason.cs
@@ -0,0 +1,27 @@
+namespace StardewModdingAPI.Framework.ModLoading
+{
+ /// <summary>Indicates why a mod could not be loaded.</summary>
+ internal enum ModFailReason
+ {
+ /// <summary>The mod has been disabled by prefixing its folder with a dot.</summary>
+ DisabledByDotConvention,
+
+ /// <summary>Multiple copies of the mod are installed.</summary>
+ Duplicate,
+
+ /// <summary>The mod has incompatible code instructions, needs a newer SMAPI version, or is marked 'assume broken' in the SMAPI metadata list.</summary>
+ Incompatible,
+
+ /// <summary>The mod's manifest is missing or invalid.</summary>
+ InvalidManifest,
+
+ /// <summary>The mod was deemed compatible, but SMAPI failed w