diff options
-rw-r--r-- | src/SMAPI/Framework/Reflection/InterfaceProxyBuilder.cs | 46 |
1 files changed, 29 insertions, 17 deletions
diff --git a/src/SMAPI/Framework/Reflection/InterfaceProxyBuilder.cs b/src/SMAPI/Framework/Reflection/InterfaceProxyBuilder.cs index b1b3c451..3c6d7c61 100644 --- a/src/SMAPI/Framework/Reflection/InterfaceProxyBuilder.cs +++ b/src/SMAPI/Framework/Reflection/InterfaceProxyBuilder.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; -using HarmonyLib; namespace StardewModdingAPI.Framework.Reflection { @@ -210,18 +209,17 @@ namespace StardewModdingAPI.Framework.Reflection methodBuilder.SetReturnType(returnType); // set up parameters + var targetParameters = target.GetParameters(); Type[] argTypes = proxy.GetParameters() .Select(a => a.ParameterType) .Select(t => t.IsGenericMethodParameter ? genericTypeParameterBuilders[t.GenericParameterPosition] : t) .ToArray(); - methodBuilder.SetParameters(argTypes); // proxy additional types string returnValueProxyTypeName = null; string[] parameterProxyTypeNames = new string[argTypes.Length]; if (positionsToProxy.Count > 0) { - var targetParameters = target.GetParameters(); foreach (int? position in positionsToProxy) { // we don't check for generics here, because earlier code does and generic positions won't end up here @@ -247,16 +245,21 @@ namespace StardewModdingAPI.Framework.Reflection } methodBuilder.SetReturnType(returnType); - methodBuilder.SetParameters(argTypes); } + methodBuilder.SetParameters(argTypes); + for (int i = 0; i < argTypes.Length; i++) + methodBuilder.DefineParameter(i, targetParameters[i].Attributes, targetParameters[i].Name); + // create method body { ILGenerator il = methodBuilder.GetILGenerator(); - LocalBuilder[] outLocals = new LocalBuilder[argTypes.Length]; + LocalBuilder[] outInputLocals = new LocalBuilder[argTypes.Length]; + LocalBuilder[] outOutputLocals = new LocalBuilder[argTypes.Length]; // calling the proxied method - var resultLocal = il.DeclareLocal(typeof(object)); // we store both unmodified and modified in here, hence `object` + LocalBuilder resultInputLocal = target.ReturnType == typeof(void) ? null : il.DeclareLocal(target.ReturnType); + LocalBuilder resultOutputLocal = returnType == typeof(void) ? null : il.DeclareLocal(returnType); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, instanceField); for (int i = 0; i < argTypes.Length; i++) @@ -268,28 +271,35 @@ namespace StardewModdingAPI.Framework.Reflection else { // previous code already checks if the parameters are specifically `out` - outLocals[i] = il.DeclareLocal(typeof(object)); // we store both unmodified and modified in here, hence `object` - il.Emit(OpCodes.Ldloca_S, outLocals[i]); + outInputLocals[i] = il.DeclareLocal(targetParameters[i].ParameterType.GetNonRefType()); + outOutputLocals[i] = il.DeclareLocal(argTypes[i].GetNonRefType()); + il.Emit(OpCodes.Ldloca, outInputLocals[i]); } } il.Emit(OpCodes.Callvirt, target); - il.Emit(OpCodes.Stloc, resultLocal); + if (target.ReturnType != typeof(void)) + il.Emit(OpCodes.Stloc, resultInputLocal); - void ProxyNonNullIfNeeded(LocalBuilder local, string proxyTypeName) + void ProxyIfNeededAndStore(LocalBuilder inputLocal, LocalBuilder outputLocal, string proxyTypeName) { if (proxyTypeName == null) + { + il.Emit(OpCodes.Ldloc, inputLocal); + il.Emit(OpCodes.Stloc, outputLocal); return; + } var isNullLabel = il.DefineLabel(); - il.Emit(OpCodes.Ldloc, local); + il.Emit(OpCodes.Ldloc, inputLocal); il.Emit(OpCodes.Brfalse, isNullLabel); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, glueField); il.Emit(OpCodes.Ldstr, proxyTypeName); - il.Emit(OpCodes.Ldloc, local); + il.Emit(OpCodes.Ldloc, inputLocal); il.Emit(OpCodes.Call, CreateInstanceForProxyTypeNameMethod); - il.Emit(OpCodes.Stloc, local); + il.Emit(OpCodes.Castclass, outputLocal.LocalType); + il.Emit(OpCodes.Stloc, outputLocal); il.MarkLabel(isNullLabel); } @@ -301,17 +311,19 @@ namespace StardewModdingAPI.Framework.Reflection continue; // previous code already checks if the parameters are specifically `out` - ProxyNonNullIfNeeded(outLocals[i], parameterProxyTypeNames[i]); + ProxyIfNeededAndStore(outInputLocals[i], outOutputLocals[i], parameterProxyTypeNames[i]); il.Emit(OpCodes.Ldarg, i + 1); - il.Emit(OpCodes.Ldloc, outLocals[i]); + il.Emit(OpCodes.Ldloc, outOutputLocals[i]); il.Emit(OpCodes.Stind_Ref); } // proxying return value - ProxyNonNullIfNeeded(resultLocal, returnValueProxyTypeName); + if (target.ReturnType != typeof(void)) + ProxyIfNeededAndStore(resultInputLocal, resultOutputLocal, returnValueProxyTypeName); // return result - il.Emit(OpCodes.Ldloc, resultLocal); + if (target.ReturnType != typeof(void)) + il.Emit(OpCodes.Ldloc, resultOutputLocal); il.Emit(OpCodes.Ret); } } |