Class MethodEditor

java.lang.Object
com.ibm.wala.shrike.shrikeBT.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 Details

    • 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 Details

    • 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.
    • allocateLabel

      public int allocateLabel() throws IllegalArgumentException
      Allocate a fresh label. This must be called during a pass and not during code emission.
      Throws:
      IllegalArgumentException
    • 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.

    • replaceWith

      public void replaceWith(int i, MethodEditor.Patch p) throws NullPointerException
      Insert code to replace the instruction. Exception handlers that cover the instruction will cover the patch.

      Multiple replacements are not allowed.

      Throws:
      NullPointerException - if p is null
      IllegalArgumentException - if p is null
    • 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.