From b9a9fe36bbaa1357b98a117400a62fecc0fc56cb Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 25 Aug 2020 22:05:27 -0400 Subject: [PATCH] fix missing-parameter rewriter not loading default values onto stack --- ...hodWithMissingOptionalParameterRewriter.cs | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/MethodWithMissingOptionalParameterRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/MethodWithMissingOptionalParameterRewriter.cs index 9db3c3fd..75182890 100644 --- a/src/SMAPI/Framework/ModLoading/Rewriters/MethodWithMissingOptionalParameterRewriter.cs +++ b/src/SMAPI/Framework/ModLoading/Rewriters/MethodWithMissingOptionalParameterRewriter.cs @@ -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; } + + /// Get the CIL instruction to load a value onto the stack. + /// The constant value to inject. + /// Returns the instruction, or null if the value type isn't supported. + 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 + }; + } } }