Skip to content

Commit

Permalink
Merge pull request #500 from LoganDark/local-index
Browse files Browse the repository at this point in the history
Methods for getting indices and making loads and stores
  • Loading branch information
pardeike authored Oct 4, 2022
2 parents 7c46762 + 679408e commit 5f76f1c
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 0 deletions.
74 changes: 74 additions & 0 deletions Harmony/Public/CodeInstruction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,80 @@ public static CodeInstruction StoreField(Type type, string name)
return new CodeInstruction(field.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, field);
}

// --- LOCALS

/// <summary>Creates a CodeInstruction loading a local with the given index, using the shorter forms when possible</summary>
/// <param name="index">The index where the local is stored</param>
/// <param name="useAddress">Use address of local</param>
/// <returns></returns>
/// <seealso cref="CodeInstructionExtensions.LocalIndex(CodeInstruction)"/>
public static CodeInstruction LoadLocal(int index, bool useAddress = false)
{
if (useAddress)
{
if (index < 256) return new CodeInstruction(OpCodes.Ldloca_S, Convert.ToByte(index));
else return new CodeInstruction(OpCodes.Ldloca, index);
}
else
{
if (index == 0) return new CodeInstruction(OpCodes.Ldloc_0);
else if (index == 1) return new CodeInstruction(OpCodes.Ldloc_1);
else if (index == 2) return new CodeInstruction(OpCodes.Ldloc_2);
else if (index == 3) return new CodeInstruction(OpCodes.Ldloc_3);
else if (index < 256) return new CodeInstruction(OpCodes.Ldloc_S, Convert.ToByte(index));
else return new CodeInstruction(OpCodes.Ldloc, index);
}
}

/// <summary>Creates a CodeInstruction storing to a local with the given index, using the shorter forms when possible</summary>
/// <param name="index">The index where the local is stored</param>
/// <returns></returns>
/// <seealso cref="CodeInstructionExtensions.LocalIndex(CodeInstruction)"/>
public static CodeInstruction StoreLocal(int index)
{
if (index == 0) return new CodeInstruction(OpCodes.Stloc_0);
else if (index == 1) return new CodeInstruction(OpCodes.Stloc_1);
else if (index == 2) return new CodeInstruction(OpCodes.Stloc_2);
else if (index == 3) return new CodeInstruction(OpCodes.Stloc_3);
else if (index < 256) return new CodeInstruction(OpCodes.Stloc_S, Convert.ToByte(index));
else return new CodeInstruction(OpCodes.Stloc, index);
}

// --- ARGUMENTS

/// <summary>Creates a CodeInstruction loading an argument with the given index, using the shorter forms when possible</summary>
/// <param name="index">The index of the argument</param>
/// <param name="useAddress">Use address of argument</param>
/// <returns></returns>
/// <seealso cref="CodeInstructionExtensions.ArgumentIndex(CodeInstruction)"/>
public static CodeInstruction LoadArgument(int index, bool useAddress = false)
{
if (useAddress)
{
if (index < 256) return new CodeInstruction(OpCodes.Ldarga_S, Convert.ToByte(index));
else return new CodeInstruction(OpCodes.Ldarga, index);
}
else
{
if (index == 0) return new CodeInstruction(OpCodes.Ldarg_0);
else if (index == 1) return new CodeInstruction(OpCodes.Ldarg_1);
else if (index == 2) return new CodeInstruction(OpCodes.Ldarg_2);
else if (index == 3) return new CodeInstruction(OpCodes.Ldarg_3);
else if (index < 256) return new CodeInstruction(OpCodes.Ldarg_S, Convert.ToByte(index));
else return new CodeInstruction(OpCodes.Ldarg, index);
}
}

/// <summary>Creates a CodeInstruction storing to an argument with the given index, using the shorter forms when possible</summary>
/// <param name="index">The index of the argument</param>
/// <returns></returns>
/// <seealso cref="CodeInstructionExtensions.ArgumentIndex(CodeInstruction)"/>
public static CodeInstruction StoreArgument(int index)
{
if (index < 256) return new CodeInstruction(OpCodes.Starg_S, Convert.ToByte(index));
else return new CodeInstruction(OpCodes.Starg, index);
}

// --- TOSTRING

/// <summary>Returns a string representation of the code instruction</summary>
Expand Down
100 changes: 100 additions & 0 deletions Harmony/Tools/CodeMatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,106 @@ internal bool Matches(List<CodeInstruction> codes, CodeInstruction instruction)
return true;
}

/// <summary>Creates a code match for local loads</summary>
/// <param name="useAddress">Whether to match for address loads</param>
/// <param name="name">An optional name</param>
/// <returns></returns>
public static CodeMatch LoadsLocal(bool useAddress = false, string name = null)
{
CodeMatch match = new CodeMatch(null, null, name);

if (useAddress)
{
match.opcodes.AddRange(new[]
{
OpCodes.Ldloca_S,
OpCodes.Ldloca
});
}
else
{
match.opcodes.AddRange(new[]
{
OpCodes.Ldloc_0,
OpCodes.Ldloc_1,
OpCodes.Ldloc_2,
OpCodes.Ldloc_3,
OpCodes.Ldloc_S,
OpCodes.Ldloc
});
}

return match;
}

/// <summary>Creates a code match for local stores</summary>
/// <param name="name">An optional name</param>
/// <returns></returns>
public static CodeMatch StoresLocal(string name = null)
{
CodeMatch match = new CodeMatch(null, null, name);

match.opcodes.AddRange(new[]
{
OpCodes.Stloc_0,
OpCodes.Stloc_1,
OpCodes.Stloc_2,
OpCodes.Stloc_3,
OpCodes.Stloc_S,
OpCodes.Stloc
});

return match;
}

/// <summary>Creates a code match for argument loads</summary>
/// <param name="useAddress">Whether to match for address loads</param>
/// <param name="name">An optional name</param>
/// <returns></returns>
public static CodeMatch LoadsArgument(bool useAddress = false, string name = null)
{
CodeMatch match = new CodeMatch(null, null, name);

if (useAddress)
{
match.opcodes.AddRange(new[]
{
OpCodes.Ldarga_S,
OpCodes.Ldarga
});
}
else
{
match.opcodes.AddRange(new[]
{
OpCodes.Ldarg_0,
OpCodes.Ldarg_1,
OpCodes.Ldarg_2,
OpCodes.Ldarg_3,
OpCodes.Ldarg_S,
OpCodes.Ldarg
});
}

return match;
}

/// <summary>Creates a code match for argument stores</summary>
/// <param name="name">An optional name</param>
/// <returns></returns>
public static CodeMatch StoresArgument(string name = null)
{
CodeMatch match = new CodeMatch(null, null, name);

match.opcodes.AddRange(new[]
{
OpCodes.Starg_S,
OpCodes.Starg
});

return match;
}

/// <summary>Returns a string that represents the match</summary>
/// <returns>A string representation</returns>
///
Expand Down
34 changes: 34 additions & 0 deletions Harmony/Tools/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,40 @@ public static bool StoresField(this CodeInstruction code, FieldInfo field)
return code.opcode == stfldCode && Equals(code.operand, field);
}

/// <summary>Returns the index targeted by this <c>ldloc</c>, <c>ldloca</c>, or <c>stloc</c></summary>
/// <param name="code">The <see cref="CodeInstruction"/></param>
/// <returns>The index it targets</returns>
/// <seealso cref="CodeInstruction.LoadLocal(int, bool)"/>
/// <seealso cref="CodeInstruction.StoreLocal(int)"/>
public static int LocalIndex(this CodeInstruction code)
{
if (code.opcode == OpCodes.Ldloc_0 || code.opcode == OpCodes.Stloc_0) return 0;
else if (code.opcode == OpCodes.Ldloc_1 || code.opcode == OpCodes.Stloc_1) return 1;
else if (code.opcode == OpCodes.Ldloc_2 || code.opcode == OpCodes.Stloc_2) return 2;
else if (code.opcode == OpCodes.Ldloc_3 || code.opcode == OpCodes.Stloc_3) return 3;
else if (code.opcode == OpCodes.Ldloc_S || code.opcode == OpCodes.Ldloc) return Convert.ToInt32(code.operand);
else if (code.opcode == OpCodes.Stloc_S || code.opcode == OpCodes.Stloc) return Convert.ToInt32(code.operand);
else if (code.opcode == OpCodes.Ldloca_S || code.opcode == OpCodes.Ldloca) return Convert.ToInt32(code.operand);
else throw new ArgumentException("Instruction is not a load or store", "code");
}

/// <summary>Returns the index targeted by this <c>ldarg</c>, <c>ldarga</c>, or <c>starg</c></summary>
/// <param name="code">The <see cref="CodeInstruction"/></param>
/// <returns>The index it targets</returns>
/// <seealso cref="CodeInstruction.LoadArgument(int, bool)"/>
/// <seealso cref="CodeInstruction.StoreArgument(int)"/>
public static int ArgumentIndex(this CodeInstruction code)
{
if (code.opcode == OpCodes.Ldarg_0) return 0;
else if (code.opcode == OpCodes.Ldarg_1) return 1;
else if (code.opcode == OpCodes.Ldarg_2) return 2;
else if (code.opcode == OpCodes.Ldarg_3) return 3;
else if (code.opcode == OpCodes.Ldarg_S || code.opcode == OpCodes.Ldarg) return Convert.ToInt32(code.operand);
else if (code.opcode == OpCodes.Starg_S || code.opcode == OpCodes.Starg) return Convert.ToInt32(code.operand);
else if (code.opcode == OpCodes.Ldarga_S || code.opcode == OpCodes.Ldarga) return Convert.ToInt32(code.operand);
else throw new ArgumentException("Instruction is not a load or store", "code");
}

/// <summary>Adds labels to the code instruction and return it</summary>
/// <param name="code">The <see cref="CodeInstruction"/></param>
/// <param name="labels">One or several <see cref="Label"/> to add</param>
Expand Down

0 comments on commit 5f76f1c

Please sign in to comment.