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

Improve CodeMatcher exceptions on invalid positions #85

Merged
merged 2 commits into from
Dec 20, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 44 additions & 18 deletions Harmony/Tools/CodeMatcher/CodeMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ private void SetOutOfBounds(int direction)
Pos = direction > 0 ? Length : -1;
}

private int PosOrThrow()
{
if (IsInvalid) throw new InvalidOperationException("Current position is out of bounds");
return Pos;
}

/// <summary>Gets the number of code instructions in this matcher</summary>
/// <value>The count</value>
///
Expand All @@ -52,27 +58,27 @@ private void SetOutOfBounds(int direction)
/// <summary>Gets the remaining code instructions</summary>
/// <value>The remaining count</value>
///
public int Remaining => Length - Math.Max(0, Pos);
public int Remaining => Length - PosOrThrow();

/// <summary>Gets the opcode at the current position</summary>
/// <value>The opcode</value>
///
public ref OpCode Opcode => ref codes[Pos].opcode;
public ref OpCode Opcode => ref codes[PosOrThrow()].opcode;

/// <summary>Gets the operand at the current position</summary>
/// <value>The operand</value>
///
public ref object Operand => ref codes[Pos].operand;
public ref object Operand => ref codes[PosOrThrow()].operand;

/// <summary>Gets the labels at the current position</summary>
/// <value>The labels</value>
///
public ref List<Label> Labels => ref codes[Pos].labels;
public ref List<Label> Labels => ref codes[PosOrThrow()].labels;

/// <summary>Gets the exception blocks at the current position</summary>
/// <value>The blocks</value>
///
public ref List<ExceptionBlock> Blocks => ref codes[Pos].blocks;
public ref List<ExceptionBlock> Blocks => ref codes[PosOrThrow()].blocks;

/// <summary>Creates an empty code matcher</summary>
public CodeMatcher()
Expand All @@ -85,6 +91,7 @@ public CodeMatcher()
///
public CodeMatcher(IEnumerable<CodeInstruction> instructions, ILGenerator generator = null)
{
if (instructions == null) throw new ArgumentNullException(nameof(instructions));
this.generator = generator;
codes = instructions.Select(c => new CodeInstruction(c)).ToList();
}
Expand All @@ -103,15 +110,18 @@ public CodeMatcher Clone()
/// <summary>Gets instructions at the current position</summary>
/// <value>The instruction</value>
///
public CodeInstruction Instruction => codes[Pos];
public CodeInstruction Instruction => codes[PosOrThrow()];

/// <summary>Gets instructions at the current position with offset</summary>
/// <param name="offset">The offset</param>
/// <returns>The instruction</returns>
///
public CodeInstruction InstructionAt(int offset)
{
return codes[Pos + offset];
var pos = PosOrThrow();
var newPos = pos + offset;
if (newPos < 0 || newPos >= Length) throw new ArgumentOutOfRangeException(nameof(offset), "offset causes position to go out of bounds");
return codes[newPos];
}

/// <summary>Gets all instructions</summary>
Expand All @@ -136,7 +146,7 @@ public IEnumerable<CodeInstruction> InstructionEnumeration()
///
public List<CodeInstruction> Instructions(int count)
{
return codes.GetRange(Pos, count).Select(c => new CodeInstruction(c)).ToList();
return codes.GetRange(PosOrThrow(), count).Select(c => new CodeInstruction(c)).ToList();
}

/// <summary>Gets all instructions within a range</summary>
Expand All @@ -163,7 +173,12 @@ public List<CodeInstruction> InstructionsInRange(int start, int end)
///
public List<CodeInstruction> InstructionsWithOffsets(int startOffset, int endOffset)
{
return InstructionsInRange(Pos + startOffset, Pos + endOffset);
var pos = PosOrThrow();
var startPos = pos + startOffset;
if (startPos < 0 || startPos >= Length) throw new ArgumentOutOfRangeException(nameof(startOffset), "startOffset causes position to go out of bounds");
var endPos = pos + endOffset;
if (endPos < 0 || endPos >= Length) throw new ArgumentOutOfRangeException(nameof(endOffset), "endOffset causes position to go out of bounds");
return InstructionsInRange(startPos, endPos);
}

/// <summary>Gets a list of all distinct labels</summary>
Expand Down Expand Up @@ -276,7 +291,7 @@ public CodeMatcher ThrowIfFalse(string explanation, Func<CodeMatcher, bool> stat
///
public CodeMatcher SetInstruction(CodeInstruction instruction)
{
codes[Pos] = instruction;
codes[PosOrThrow()] = instruction;
return this;
}

Expand Down Expand Up @@ -388,6 +403,7 @@ public CodeMatcher AddLabels(IEnumerable<Label> labels)
///
public CodeMatcher AddLabelsAt(int position, IEnumerable<Label> labels)
{
if (position < 0 || position >= Length) throw new ArgumentOutOfRangeException(nameof(position), "position is out of bounds");
codes[position].labels.AddRange(labels);
return this;
}
Expand All @@ -410,7 +426,10 @@ public CodeMatcher SetJumpTo(OpCode opcode, int destination, out Label label)
///
public CodeMatcher Insert(params CodeInstruction[] instructions)
{
codes.InsertRange(Pos, instructions);
if (Pos == Length)
codes.AddRange(instructions);
else
codes.InsertRange(PosOrThrow(), instructions);
return this;
}

Expand All @@ -420,7 +439,10 @@ public CodeMatcher Insert(params CodeInstruction[] instructions)
///
public CodeMatcher Insert(IEnumerable<CodeInstruction> instructions)
{
codes.InsertRange(Pos, instructions);
if (Pos == Length)
codes.AddRange(instructions);
else
codes.InsertRange(PosOrThrow(), instructions);
return this;
}

Expand All @@ -431,8 +453,9 @@ public CodeMatcher Insert(IEnumerable<CodeInstruction> instructions)
///
public CodeMatcher InsertBranch(OpCode opcode, int destination)
{
var pos = PosOrThrow();
_ = CreateLabelAt(destination, out var label);
codes.Insert(Pos, new CodeInstruction(opcode, label));
codes.Insert(pos, new CodeInstruction(opcode, label));
return this;
}

Expand Down Expand Up @@ -479,7 +502,7 @@ public CodeMatcher InsertBranchAndAdvance(OpCode opcode, int destination)
///
public CodeMatcher RemoveInstruction()
{
codes.RemoveAt(Pos);
codes.RemoveAt(PosOrThrow());
return this;
}

Expand All @@ -489,7 +512,7 @@ public CodeMatcher RemoveInstruction()
///
public CodeMatcher RemoveInstructions(int count)
{
codes.RemoveRange(Pos, count);
codes.RemoveRange(PosOrThrow(), count);
return this;
}

Expand All @@ -500,6 +523,9 @@ public CodeMatcher RemoveInstructions(int count)
///
public CodeMatcher RemoveInstructionsInRange(int start, int end)
{
if (start < 0 || start >= Length) throw new ArgumentOutOfRangeException(nameof(start), "start is out of bounds");
if (end < 0 || end >= Length) throw new ArgumentOutOfRangeException(nameof(end), "end is out of bounds");

if (start > end)
{
(start, end) = (end, start);
Expand Down Expand Up @@ -578,7 +604,7 @@ public CodeMatcher SearchBackwards(Func<CodeInstruction, bool> predicate)

private CodeMatcher Search(Func<CodeInstruction, bool> predicate, int direction)
{
FixStart();
FixStart(); //todo: Should invalid position throw instead? Breaking change
while (IsValid && predicate(Instruction) == false)
Pos += direction;
lastError = IsInvalid ? $"Cannot find {predicate}" : null;
Expand Down Expand Up @@ -642,9 +668,9 @@ public CodeMatcher MatchEndBackwards(params CodeMatch[] matches)

private CodeMatcher Match(CodeMatch[] matches, int direction, bool useEnd)
{
lastMatchCall = delegate()
lastMatchCall = delegate ()
{
FixStart();
FixStart(); //todo: Should invalid position throw instead? Breaking change
while (IsValid)
{
if (MatchSequence(Pos, matches))
Expand Down
Loading