Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Methods for getting indices and making loads and stores #500

Merged
merged 3 commits into from Oct 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
74 changes: 74 additions & 0 deletions Harmony/Public/CodeInstruction.cs
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
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
});
}

LoganDark marked this conversation as resolved.
Show resolved Hide resolved
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
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