Class MethodEditor


  • public final class MethodEditor
    extends Object
    The MethodEditor is the core of the ShrikeBT code rewriting mechanism. To rewrite code, construct a MethodEditor initialized with the intial code for the method. Then perform a series of passes. In each pass you call beginPass(), insert a number of patches using the insert...() or replace...() methods, then optionally call applyPatches() to update the code with your changes, then call endPass(). The end of each pass updates the code in the MethodData that you passed in, so the new code can be extracted from that MethodData object. Note that if applyPatches() is not called, or it is called but no patches had been inserted, then the code will not be updated and that pass is essentially aborted. A patch is simply a subclass of MethodEditor.Patch, representing a code sequence to insert into the method. Each patch class implements one method, emitTo(), which writes the patch code into the code stream using the provided MethodEditor.Output instance. Anonymous inner classes are very useful for writing patches. Patches can be inserted at the following points:
    • Before the start of the method
    • Before or after a particular instruction
    • Replacing a particular instruction
    • Handling an exception on a particular instruction
    • Handling an exception anywhere in the method
    • After the end of the method, where code will not normally be executed, but can be branched to by another patch
    Patch application is deterministic; if two patches are applied at the same point, then the order of application determines the order of code generation, in a way specified by the particular application point. See the patch application methods below. MethodEditor relies on labels. A label is an integer representing a point in the code. Labels are valid only during a single pass; at the end of each pass, instructions are reordered and old labels become invalid. At the beginning of a pass every instruction in the instructions array is labelled with the index of that instruction in the array. During instrumentation new labels can be allocated by calling MethodEditor.allocateLabel(); control instructions can be created referring to these new labels or the existing labels. At the end of a pass, as patch code is spliced into the method body, all instructions are updated to refer to the new labels which are simply the indices of instructions in the instruction array.
    • Constructor Detail

      • MethodEditor

        public MethodEditor​(MethodData info)
        Build an editor for the given method. This editor will write back its changes to the method info.
        Throws:
        IllegalArgumentException - if info is null
      • MethodEditor

        public MethodEditor​(Instruction[] instructions,
                            ExceptionHandler[][] handlers,
                            int[] instructionsToBytecodes)
        Build an editor for specific method data. After patching the code you can retrieve the new code, handlers and instructions-to-bytecode-offsets map.
    • Method Detail

      • getHandlers

        public ExceptionHandler[][] getHandlers()
        Returns:
        the current handler array
      • getInstructions

        public IInstruction[] getInstructions()
        Returns:
        the current instruction array
      • getInstructionsToBytecodes

        public int[] getInstructionsToBytecodes()
        Returns:
        the current instructions-to-bytecode-offsets map
      • beginPass

        public void beginPass()
        This must be called before inserting any patches.
      • endPass

        public void endPass()
        This must be called after inserting any patches.
      • insertAtStart

        public void insertAtStart​(MethodEditor.Patch p)
        Insert code to be executed whenever the method is entered. This code is not protected by any exception handlers (other than handlers declared in the patch). When multiple 'start' patches are given, the last one added is first in execution order.
        Throws:
        IllegalArgumentException - if p is null
      • insertBefore

        public void insertBefore​(int i,
                                 MethodEditor.Patch p)
        Insert code to be executed before the instruction. Branches to the instruction will branch to this code. Exception handlers that cover the instruction will be extended to cover the patch. When multiple 'before' patches are given, the last one added is first in execution order.
        Throws:
        IllegalArgumentException - if p is null
      • insertAfter

        public void insertAfter​(int i,
                                MethodEditor.Patch p)
        Insert code to be executed after the instruction. This code will only execute if the instruction "falls through". For example, code inserted after a "goto" will never be executed. Likewise if the instruction throws an execution the 'after' code will not be executed. Exception handlers that cover the instruction will be extended to cover the patch. When multiple 'after' patches are given, the last one added is LAST in execution order.
      • addInstructionExceptionHandler

        public void addInstructionExceptionHandler​(int i,
                                                   String catchClass,
                                                   MethodEditor.Patch p)
        An "instruction exception handler" handles exceptions generated by a specific instruction (including patch code that may be inserted before, after, or instead of the instruction in this pass). If the patch code falls through, control resumes with the next instruction. This exception handler handles exceptions before any exception handler already attached to the instruction. Furthermore, the patch itself is covered by the exception handlers already attached to the instruction. If multiple instruction exception handlers are given, then the last one added handles the exception first; if an exception is rethrown, then the next-to-last one added handles that exception, etc.
      • addMethodExceptionHandler

        public void addMethodExceptionHandler​(String catchClass,
                                              MethodEditor.Patch p)
        A "method exception handler" handles exceptions generated anywhere in the method. The patch code must not fall through; it must return or throw an exception. If multiple method exception handlers are given, then the last one added handles the exception first; if an exception is rethrown, then the next-to-last one added handles that exception, etc.
      • insertAfterBody

        public void insertAfterBody​(MethodEditor.Patch p)
        This method inserts code that will be placed after the method body. This code will not be executed normally, but it can emit label definitions that other patches can branch to. No exception handlers cover this code (other than exception handlers emitted by patch p itself).
        Throws:
        IllegalArgumentException - if p is null
      • getData

        public MethodData getData()
        Returns:
        the MethodData used to create this editor, or null if no MethodData is linked to this editor
      • applyPatches

        public boolean applyPatches()
                             throws IllegalArgumentException
        This method finishes a pass. All code is updated; instructions are reordered and old labels may not be valid. If no patches were issued, we don't need to do anything at all; this case is detected quickly and no updates are made.
        Returns:
        true iff non-trivial patches were applied
        Throws:
        IllegalArgumentException
      • visitInstructions

        public void visitInstructions​(MethodEditor.Visitor v)
        Apply Visitor v to each instruction in the code, for the purpose of patching the code.