|
8 | 8 | namespace HarmonyLib |
9 | 9 | { |
10 | 10 | /// <summary>A CodeInstruction matcher</summary> |
11 | | - /// |
| 11 | + /// |
12 | 12 | public class CodeMatcher |
13 | 13 | { |
14 | 14 | private readonly ILGenerator generator; |
@@ -69,7 +69,7 @@ public class CodeMatcher |
69 | 69 | public ref List<ExceptionBlock> Blocks => ref codes[Pos].blocks; |
70 | 70 |
|
71 | 71 | /// <summary>Creates an empty code matcher</summary> |
72 | | - /// |
| 72 | + /// |
73 | 73 | public CodeMatcher() |
74 | 74 | { |
75 | 75 | } |
@@ -255,6 +255,18 @@ public CodeMatcher ThrowIfFalse(string explanation, Func<CodeMatcher, bool> stat |
255 | 255 | return this; |
256 | 256 | } |
257 | 257 |
|
| 258 | + /// <summary>Runs some code when chaining <see cref="CodeMatcher"/> at the current position</summary> |
| 259 | + /// <param name="action">The <see cref="Action{CodeMatcher}"/> to run</param> |
| 260 | + /// <returns>The same code matcher</returns> |
| 261 | + /// |
| 262 | + public CodeMatcher Do(Action<CodeMatcher> action) |
| 263 | + { |
| 264 | + if (action == null) |
| 265 | + throw new ArgumentNullException(nameof(action)); |
| 266 | + action(this); |
| 267 | + return this; |
| 268 | + } |
| 269 | + |
258 | 270 | /// <summary>Sets an instruction at current position</summary> |
259 | 271 | /// <param name="instruction">The instruction to set</param> |
260 | 272 | /// <returns>The same code matcher</returns> |
@@ -657,13 +669,13 @@ public CodeMatcher End() |
657 | 669 | } |
658 | 670 |
|
659 | 671 | /// <summary>Searches forward with a predicate and advances position</summary> |
660 | | - /// <param name="predicate">The predicate</param> |
| 672 | + /// <param name="predicate">A function to test each instruction for a match</param> |
661 | 673 | /// <returns>The same code matcher</returns> |
662 | 674 | /// |
663 | 675 | public CodeMatcher SearchForward(Func<CodeInstruction, bool> predicate) => Search(predicate, 1); |
664 | 676 |
|
665 | 677 | /// <summary>Searches backwards with a predicate and reverses position</summary> |
666 | | - /// <param name="predicate">The predicate</param> |
| 678 | + /// <param name="predicate">A function to test each instruction for a match</param> |
667 | 679 | /// <returns>The same code matcher</returns> |
668 | 680 | /// |
669 | 681 | public CodeMatcher SearchBackwards(Func<CodeInstruction, bool> predicate) => Search(predicate, -1); |
@@ -725,6 +737,101 @@ private CodeMatcher Search(Func<CodeInstruction, bool> predicate, int direction) |
725 | 737 | /// |
726 | 738 | public CodeMatcher PrepareMatchEndBackwards(params CodeMatch[] matches) => Match(matches, -1, true, true); |
727 | 739 |
|
| 740 | + /// <summary>Removes instructions from the current position forward until a predicate is matched. The matched instruction is not removed</summary> |
| 741 | + /// <param name="predicate">A function to test each instruction for a match</param> |
| 742 | + /// <returns>The same code matcher</returns> |
| 743 | + public CodeMatcher RemoveSearchForward(Func<CodeInstruction, bool> predicate) |
| 744 | + { |
| 745 | + if (IsInvalid) |
| 746 | + throw new InvalidOperationException("Cannot remove instructions from an invalid position."); |
| 747 | + |
| 748 | + var originalPos = Pos; |
| 749 | + var finder = Clone().SearchForward(predicate); |
| 750 | + if (finder.IsInvalid) |
| 751 | + { |
| 752 | + lastError = finder.lastError; |
| 753 | + SetOutOfBounds(1); |
| 754 | + return this; |
| 755 | + } |
| 756 | + |
| 757 | + var end = finder.Pos - 1; // stop before the matching instruction |
| 758 | + if (end >= originalPos) |
| 759 | + _ = RemoveInstructionsInRange(originalPos, end); |
| 760 | + return this; |
| 761 | + } |
| 762 | + |
| 763 | + /// <summary>Removes instructions from the current position backward until a predicate is matched. The matched instruction is not removed</summary> |
| 764 | + /// <param name="predicate">A function to test each instruction for a match</param> |
| 765 | + /// <returns>The same code matcher</returns> |
| 766 | + public CodeMatcher RemoveSearchBackward(Func<CodeInstruction, bool> predicate) |
| 767 | + { |
| 768 | + if (IsInvalid) |
| 769 | + throw new InvalidOperationException("Cannot remove instructions from an invalid position."); |
| 770 | + |
| 771 | + var originalPos = Pos; |
| 772 | + var finder = Clone().SearchBackwards(predicate); |
| 773 | + if (finder.IsInvalid) |
| 774 | + { |
| 775 | + lastError = finder.lastError; |
| 776 | + SetOutOfBounds(-1); |
| 777 | + return this; |
| 778 | + } |
| 779 | + |
| 780 | + var matchPos = finder.Pos; |
| 781 | + var start = matchPos + 1; |
| 782 | + if (originalPos >= start) |
| 783 | + _ = RemoveInstructionsInRange(start, originalPos); |
| 784 | + Pos = matchPos; |
| 785 | + return this; |
| 786 | + } |
| 787 | + |
| 788 | + /// <summary>Removes instructions from the current position up to the next match (exclusive)</summary> |
| 789 | + /// <param name="matches">Some code matches</param> |
| 790 | + /// <returns>The same code matcher</returns> |
| 791 | + public CodeMatcher RemoveUntilForward(params CodeMatch[] matches) |
| 792 | + { |
| 793 | + if (IsInvalid) |
| 794 | + throw new InvalidOperationException("Cannot remove instructions from an invalid position."); |
| 795 | + |
| 796 | + var originalPos = Pos; |
| 797 | + var finder = Clone().MatchStartForward(matches); |
| 798 | + if (finder.IsInvalid) |
| 799 | + { |
| 800 | + lastError = finder.lastError; |
| 801 | + SetOutOfBounds(1); |
| 802 | + return this; |
| 803 | + } |
| 804 | + |
| 805 | + var end = finder.Pos - 1; |
| 806 | + if (end >= originalPos) |
| 807 | + _ = RemoveInstructionsInRange(originalPos, end); |
| 808 | + return this; |
| 809 | + } |
| 810 | + |
| 811 | + /// <summary>Removes instructions backwards from the current position to the previous match (exclusive)</summary> |
| 812 | + /// <param name="matches">Some code matches</param> |
| 813 | + /// <returns>The same code matcher</returns> |
| 814 | + public CodeMatcher RemoveUntilBackward(params CodeMatch[] matches) |
| 815 | + { |
| 816 | + if (IsInvalid) |
| 817 | + throw new InvalidOperationException("Cannot remove instructions from an invalid position."); |
| 818 | + |
| 819 | + var originalPos = Pos; |
| 820 | + var finder = Clone().MatchEndBackwards(matches); |
| 821 | + if (finder.IsInvalid) |
| 822 | + { |
| 823 | + lastError = finder.lastError; |
| 824 | + SetOutOfBounds(-1); |
| 825 | + return this; |
| 826 | + } |
| 827 | + |
| 828 | + var start = finder.Pos; |
| 829 | + if (originalPos > start) |
| 830 | + _ = RemoveInstructionsInRange(start + 1, originalPos); |
| 831 | + Pos = start; |
| 832 | + return this; |
| 833 | + } |
| 834 | + |
728 | 835 | private CodeMatcher Match(CodeMatch[] matches, int direction, bool useEnd, bool prepareOnly) |
729 | 836 | { |
730 | 837 | lastMatchCall = delegate () |
|
0 commit comments