fix missing-parameter rewriter not loading default values onto stack

This commit is contained in:
Jesse Plamondon-Willard 2020-08-25 22:05:27 -04:00
parent 915e6d22f1
commit b9a9fe36bb
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
1 changed files with 36 additions and 4 deletions

View File

@ -68,14 +68,28 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
if (method == null)
return false;
// add extra parameters
foreach (ParameterDefinition parameter in method.Parameters.Skip(methodRef.Parameters.Count))
// get instructions to inject
var injectables = method.Parameters.Skip(methodRef.Parameters.Count)
.Select(p => new { Parameter = p, LoadValueInstruction = this.GetLoadValueInstruction(p.Constant) })
.ToArray();
if (injectables.Any(p => p.LoadValueInstruction == null))
return false; // SMAPI needs to load the value onto the stack before the method call, but the optional parameter type wasn't recognized
// inject new parameters
foreach (var entry in injectables)
{
methodRef.Parameters.Add(new ParameterDefinition(
// load value onto stack
cil.InsertBefore(instruction, entry.LoadValueInstruction);
// add parameter
ParameterDefinition parameter = entry.Parameter;
var newParameter = new ParameterDefinition(
name: parameter.Name,
attributes: parameter.Attributes,
parameterType: module.ImportReference(parameter.ParameterType)
));
);
newParameter.Constant = parameter.Constant;
methodRef.Parameters.Add(newParameter);
}
this.Phrases.Add($"{methodRef.DeclaringType.Name}.{methodRef.Name} (added missing optional parameters)");
@ -109,5 +123,23 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
return true;
}
/// <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>
private 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
};
}
}
}