Skip to content

Commit

Permalink
Fixed EmitCallParameter bug that was calculating wrong param index in…
Browse files Browse the repository at this point in the history
… methods with "out" parameters

Fixed EmitCallParameter so it works with all possible out/ref cases.
Version bump to v1.0.7
  • Loading branch information
pardeike committed Feb 11, 2017
1 parent 1e0e90e commit 0a571a6
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 9 deletions.
61 changes: 54 additions & 7 deletions Harmony/MethodPatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,31 @@ static LocalBuilder DeclarePrivateStateVar(ILGenerator il)
return v;
}

static void EmitCallParameter(ILGenerator il, MethodBase original, MethodInfo patch, Dictionary<string, LocalBuilder> variables, bool noOut)
static OpCode LoadIndOpCodeFor(Type type)
{
if (type.IsEnum) return OpCodes.Ldind_I4;

if (type == typeof(float)) return OpCodes.Ldind_R4;
if (type == typeof(double)) return OpCodes.Ldind_R8;

if (type == typeof(byte)) return OpCodes.Ldind_U1;
if (type == typeof(ushort)) return OpCodes.Ldind_U2;
if (type == typeof(uint)) return OpCodes.Ldind_U4;
if (type == typeof(ulong)) return OpCodes.Ldind_I8;

if (type == typeof(sbyte)) return OpCodes.Ldind_I1;
if (type == typeof(short)) return OpCodes.Ldind_I2;
if (type == typeof(int)) return OpCodes.Ldind_I4;
if (type == typeof(long)) return OpCodes.Ldind_I8;

return OpCodes.Ldind_Ref;
}

static void EmitCallParameter(ILGenerator il, MethodBase original, MethodInfo patch, Dictionary<string, LocalBuilder> variables)
{
var isInstance = original.IsStatic == false;
var originalParameterNames = original.GetParameters()
.Where(p => p.IsOut == false || noOut == false).Select(p => p.Name).ToArray();
var originalParameters = original.GetParameters();
var originalParameterNames = originalParameters.Select(p => p.Name).ToArray();
foreach (var patchParam in patch.GetParameters())
{
if (patchParam.Name == INSTANCE_PARAM)
Expand Down Expand Up @@ -123,16 +143,43 @@ static void EmitCallParameter(ILGenerator il, MethodBase original, MethodInfo pa

var idx = Array.IndexOf(originalParameterNames, patchParam.Name);
if (idx == -1) throw new Exception("Parameter \"" + patchParam.Name + "\" not found in method " + original);
var ldargCode = patchParam.ParameterType.IsByRef ? OpCodes.Ldarga : OpCodes.Ldarg;
il.Emit(ldargCode, idx + (isInstance ? 1 : 0));

// original -> patch opcode
// --------------------------------------
// 1 normal -> normal : LDARG
// 2 normal -> ref/out : LDARGA
// 3 ref/out -> normal : LDARG, LDIND_x
// 4 ref/out -> ref/out : LDARG
//
var originalIsNormal = originalParameters[idx].IsOut == false && originalParameters[idx].ParameterType.IsByRef == false;
var patchIsNormal = patchParam.IsOut == false && patchParam.ParameterType.IsByRef == false;
var patchArgIndex = idx + (isInstance ? 1 : 0);

// Case 1 + 4
if (originalIsNormal == patchIsNormal)
{
il.Emit(OpCodes.Ldarg, patchArgIndex);
return;
}

// Case 2
if (originalIsNormal && patchIsNormal == false)
{
il.Emit(OpCodes.Ldarga, patchArgIndex);
return;
}

// Case 3
il.Emit(OpCodes.Ldarg, patchArgIndex);
il.Emit(LoadIndOpCodeFor(originalParameters[idx].ParameterType));
}
}

static void AddPrefixes(ILGenerator il, MethodBase original, List<MethodInfo> prefixes, Dictionary<string, LocalBuilder> variables, Label label)
{
prefixes.ForEach(fix =>
{
EmitCallParameter(il, original, fix, variables, true);
EmitCallParameter(il, original, fix, variables);
il.Emit(OpCodes.Call, fix);
if (fix.ReturnType != typeof(void))
{
Expand All @@ -147,7 +194,7 @@ static void AddPostfixes(ILGenerator il, MethodBase original, List<MethodInfo> p
{
postfixes.ForEach(fix =>
{
EmitCallParameter(il, original, fix, variables, false);
EmitCallParameter(il, original, fix, variables);
il.Emit(OpCodes.Call, fix);
if (fix.ReturnType != typeof(void))
throw new Exception("Postfix patch " + fix + " has not \"void\" return type: " + fix.ReturnType);
Expand Down
4 changes: 2 additions & 2 deletions Harmony/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.6.0")]
[assembly: AssemblyFileVersion("1.0.6.0")]
[assembly: AssemblyVersion("1.0.7.0")]
[assembly: AssemblyFileVersion("1.0.7.0")]

0 comments on commit 0a571a6

Please sign in to comment.