Class CodeWriter


  • public class CodeWriter
    extends Object
    A class for analyzing and modifying the bytecode of a MethodEntry.
    • Constructor Detail

      • CodeWriter

        public CodeWriter​(MethodEntry methodEntry)
        Constructs a CodeWriter for the given MethodEntry.
        Parameters:
        methodEntry - The MethodEntry to manipulate.
    • Method Detail

      • getMethodEntry

        public MethodEntry getMethodEntry()
        Returns:
        the method entry
      • getCodeAttribute

        public CodeAttribute getCodeAttribute()
        Returns:
        the code attribute
      • getBytecode

        public byte[] getBytecode()
        Returns:
        the bytecode
      • getConstPool

        public ConstPool getConstPool()
        Returns:
        the const pool
      • getMaxStack

        public int getMaxStack()
        Returns:
        the max stack
      • getMaxLocals

        public int getMaxLocals()
        Returns:
        the max locals
      • isModified

        public boolean isModified()
        Returns:
        true if the bytecode has been modified since loading
      • getInstructions

        public Iterable<Instruction> getInstructions()
        Iterates over all instructions.
        Returns:
        An Iterable of Instructions.
      • getInstructionList

        public List<Instruction> getInstructionList()
        Returns a fresh, bytecode-ordered, random-access snapshot of the instructions.
        Returns:
        the instructions in offset order
      • getInstructionCount

        public int getInstructionCount()
        Returns the total number of instructions in this method.
        Returns:
        instruction count
      • getInstructionIndex

        public int getInstructionIndex​(int offset)
        Gets the sequential instruction index for the instruction at the given bytecode offset.
        Parameters:
        offset - The bytecode offset
        Returns:
        The sequential instruction index, or -1 if no instruction at that offset
      • getInstructionAt

        public Instruction getInstructionAt​(int index)
        Gets the instruction at a specific sequential index (0, 1, 2, ...).
        Parameters:
        index - The sequential instruction index
        Returns:
        The instruction at that index, or null if out of bounds
      • getOffsetAt

        public int getOffsetAt​(int index)
        Gets the bytecode offset for the instruction at a specific sequential index.
        Parameters:
        index - The sequential instruction index
        Returns:
        The bytecode offset, or -1 if out of bounds
      • findInstructionIndexForOffset

        public int findInstructionIndexForOffset​(int targetOffset)
        Finds the instruction index that contains or follows the given bytecode offset.
        Parameters:
        targetOffset - The bytecode offset to search for
        Returns:
        The instruction index, or -1 if not found
      • insertInstruction

        public void insertInstruction​(int offset,
                                      Instruction newInstr)
        Inserts an instruction before whatever instruction currently sits at offset (or appends it when offset == code length).
        Parameters:
        offset - the bytecode offset of the instruction to insert before
        newInstr - the new instruction to insert
      • removeInstruction

        public void removeInstruction​(Instruction handle)
        Removes an instruction, identified by handle (object identity), and relinks the method.
        Parameters:
        handle - the instruction to remove (an object currently in this method)
      • replaceInstruction

        public void replaceInstruction​(Instruction handle,
                                       Instruction replacement)
        Replaces an instruction (by handle) with another, preserving control flow.
        Parameters:
        handle - the instruction to replace
        replacement - the new instruction
      • removeInstructions

        public void removeInstructions​(Collection<Instruction> handles)
        Removes many instructions in a single relink, linear in the method size rather than quadratic in the number of removals.
        Parameters:
        handles - the instruction handles to remove
      • replaceBody

        public void replaceBody​(List<Instruction> body)
        Replaces this method's entire instruction stream with body (e.g. a cloned/grafted body), then relinks.
        Parameters:
        body - the new instruction stream, in order
      • replaceBody

        public void replaceBody​(List<Instruction> body,
                                List<ExceptionTableEntry> exceptions)
        As replaceBody(List) but with an exception table whose entry PCs are interpreted against body's layout, bound by identity so regenerated frames cover the handler blocks.
        Parameters:
        body - the new instruction stream, in order
        exceptions - the exception-table entries (catch_type indices in this method's pool)
      • replaceBody

        public void replaceBody​(CodeWriter.ClonedRange block)
        Replaces the body with a cloned range, carrying its targets + exception regions by identity.
        Parameters:
        block - the cloned range to install as the new body
      • replaceBody

        public void replaceBody​(CodeWriter.ClonedRange block,
                                List<ExceptionTableEntry> exceptions)
        Replaces the body with a cloned range plus an explicit exception table (targets by identity).
        Parameters:
        block - the cloned range to install as the new body
        exceptions - the exception-table entries, PCs against the block's layout
      • insertBefore

        public void insertBefore​(Instruction handle,
                                 CodeWriter.ClonedRange block)
        Inserts a cloned range before the handle, carrying its targets + exception regions by identity.
        Parameters:
        handle - the instruction to insert before
        block - the cloned range to splice
      • insertAfter

        public void insertAfter​(Instruction handle,
                                CodeWriter.ClonedRange block)
        Inserts a cloned range after the handle, carrying its targets + exception regions by identity.
        Parameters:
        handle - the instruction to insert after
        block - the cloned range to splice
      • insertChainBefore

        public void insertChainBefore​(Instruction at,
                                      List<CodeWriter.ClonedRange> bodies)
        Splices several cloned bodies before at, chaining them so each body's continuation exits (e.g. from CodeWriter.ClonedRange.redirectReturns()) fall through into the next body's entry and the last body's into at.
        Parameters:
        at - the instruction the last body falls through into
        bodies - the cloned bodies to splice, in execution order
      • insertBefore

        public void insertBefore​(Instruction handle,
                                 Instruction newInstr)
        Inserts newInstr immediately before the given instruction handle.
        Parameters:
        handle - the instruction to insert before
        newInstr - the instruction to insert
      • insertBefore

        public void insertBefore​(Instruction handle,
                                 List<Instruction> block)
        Inserts a block of instructions (e.g. a cloned method body) immediately before the handle.
        Parameters:
        handle - the instruction to insert before
        block - the instructions, in order
      • insertAfter

        public void insertAfter​(Instruction handle,
                                Instruction newInstr)
        Inserts newInstr immediately after the given instruction handle.
        Parameters:
        handle - the instruction to insert after
        newInstr - the instruction to insert
      • insertAfter

        public void insertAfter​(Instruction handle,
                                List<Instruction> block)
        Inserts a block of instructions immediately after the handle.
        Parameters:
        handle - the instruction to insert after
        block - the instructions, in order
      • setBranchTarget

        public void setBranchTarget​(Instruction branch,
                                    Instruction target)
        Registers the target of a (typically newly created) branch instruction by identity, so that the relink pass can compute its relative offset.
        Parameters:
        branch - the branch instruction
        target - the instruction it jumps to
      • setSwitchTargets

        public void setSwitchTargets​(Instruction switchInstr,
                                     Instruction defaultTarget,
                                     List<Instruction> caseTargets)
        Registers the targets of a switch instruction by identity: the default target followed by one target per case in case order.
        Parameters:
        switchInstr - the switch instruction
        defaultTarget - the default branch target
        caseTargets - one target per case, in case order
      • toClonedRange

        public CodeWriter.ClonedRange toClonedRange()
        Snapshots this writer's full instruction list and its by-identity branch/switch targets into a CodeWriter.ClonedRange.
        Returns:
        the snapshot, carrying no exception regions
      • toClonedRange

        public CodeWriter.ClonedRange toClonedRange​(List<ExceptionTableEntry> regionEntries,
                                                    Map<String,​List<Integer>> externalOffsets,
                                                    List<Integer> continuationOffsets)
        As toClonedRange(List) but also records branches whose targets lie outside the snippet.
        Parameters:
        regionEntries - the exception-table entries to carry, PCs against this writer's layout
        externalOffsets - branch offsets per external label name, bound at splice time
        continuationOffsets - branch offsets that fall through into the host
        Returns:
        the snapshot
      • cloneRange

        public List<Instruction> cloneRange​(Instruction from,
                                            Instruction to,
                                            int localOffset)
        Clones a contiguous instruction range [from, to] into a fresh list, shifting every local index by localOffset.
        Parameters:
        from - first instruction of the range (a handle in this method)
        to - last instruction of the range (inclusive)
        localOffset - value added to every local-variable index in the clone
        Returns:
        the cloned instructions, in order
      • cloneRangeWithTargets

        public CodeWriter.ClonedRange cloneRangeWithTargets​(Instruction from,
                                                            Instruction to,
                                                            int localOffset,
                                                            ConstPool targetPool,
                                                            IntUnaryOperator cpRemap)
        As cloneRange(Instruction, Instruction, int), but carries branch/switch targets by identity and remaps constant-pool references into targetPool.
        Parameters:
        from - first instruction (inclusive)
        to - last instruction (inclusive)
        localOffset - value added to every local-variable index
        targetPool - the constant pool the clones will live in (null to keep this method's pool)
        cpRemap - old cp index → new cp index, or null for no remap
        Returns:
        the cloned range with identity-tracked targets
      • computeMaxStack

        public int computeMaxStack()
        Computes max_stack over the control-flow graph (via FrameGenerator) and raises the method's value if the linear estimate under-counts - correct for loops, joins, and handler entry states where a textual scan can miss the true peak.
        Returns:
        the resulting max_stack
      • analyze

        public void analyze()
        Updates maxStack and maxLocals from the current bytecode, using a simplified analysis that does not cover every case.
      • write

        public void write()
                   throws IOException
        Writes the modified bytecode back to the MethodEntry.
        Throws:
        IOException - If an I/O error occurs.
      • writeWithoutMaxStack

        public void writeWithoutMaxStack()
        Serializes the (already-relinked) instructions back to the method WITHOUT the dataflow max-stack pass.
      • hasValidStackMapTable

        public boolean hasValidStackMapTable()
        Checks if the CodeAttribute has a valid StackMapTable.
        Returns:
        true if a StackMapTable exists with frames
      • computeFrames

        public void computeFrames()
        Computes and installs StackMapTable frames after a write(); a valid existing table is preserved when the bytecode was not modified.
      • forceComputeFrames

        public void forceComputeFrames()
        Recomputes the StackMapTable unconditionally, ignoring the modified flag and any existing valid table.
      • getBytecodeSize

        public int getBytecodeSize()
        Returns the size of the bytecode array in bytes.
        Returns:
        bytecode size
      • endsWithReturn

        public boolean endsWithReturn()
        Checks if the bytecode ends with a return instruction.
        Returns:
        true if ends with return, false otherwise
      • insertInvokeVirtual

        public InvokeVirtualInstruction insertInvokeVirtual​(int offset,
                                                            int methodRefIndex)
        Inserts an INVOKEVIRTUAL instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        methodRefIndex - The index into the constant pool for the method reference.
        Returns:
        the inserted instruction
      • insertInvokeSpecial

        public InvokeSpecialInstruction insertInvokeSpecial​(int offset,
                                                            int methodRefIndex)
        Inserts an INVOKESPECIAL instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        methodRefIndex - The index into the constant pool for the method reference.
        Returns:
        the inserted instruction
      • insertInvokeStatic

        public InvokeStaticInstruction insertInvokeStatic​(int offset,
                                                          int methodRefIndex)
        Inserts an INVOKESTATIC instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        methodRefIndex - The index into the constant pool for the method reference.
        Returns:
        the inserted instruction
      • insertInvokeInterface

        public InvokeInterfaceInstruction insertInvokeInterface​(int offset,
                                                                int interfaceMethodRefIndex,
                                                                int count)
        Inserts an INVOKEINTERFACE instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        interfaceMethodRefIndex - The index into the constant pool for the interface method reference.
        count - The count of arguments for the interface method.
        Returns:
        the inserted instruction
      • insertInvokeDynamic

        public InvokeDynamicInstruction insertInvokeDynamic​(int offset,
                                                            int cpIndex)
        Inserts an INVOKEDYNAMIC instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        cpIndex - The constant pool index to the CONSTANT_InvokeDynamic_info entry.
        Returns:
        the inserted instruction
      • insertALoad

        public ALoadInstruction insertALoad​(int offset,
                                            int index)
        Inserts an ALOAD instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        index - The local variable index to load from.
        Returns:
        the inserted instruction
      • insertAStore

        public AStoreInstruction insertAStore​(int offset,
                                              int index)
        Inserts an ASTORE instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        index - The local variable index to store into.
        Returns:
        the inserted instruction
      • insertGetStatic

        public GetFieldInstruction insertGetStatic​(int offset,
                                                   int fieldRefIndex)
        Inserts a GETSTATIC instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        fieldRefIndex - The index into the constant pool for the field reference.
        Returns:
        the inserted instruction
      • insertGetField

        public GetFieldInstruction insertGetField​(int offset,
                                                  int fieldRefIndex)
        Inserts a GETFIELD instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        fieldRefIndex - The index into the constant pool for the field reference.
        Returns:
        the inserted instruction
      • insertPutStatic

        public PutFieldInstruction insertPutStatic​(int offset,
                                                   int fieldRefIndex)
        Inserts a PUTSTATIC instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        fieldRefIndex - The index into the constant pool for the field reference.
        Returns:
        the inserted instruction
      • insertPutField

        public PutFieldInstruction insertPutField​(int offset,
                                                  int fieldRefIndex)
        Inserts a PUTFIELD instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        fieldRefIndex - The index into the constant pool for the field reference.
        Returns:
        the inserted instruction
      • insertILoad

        public ILoadInstruction insertILoad​(int offset,
                                            int index)
        Inserts an ILOAD instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        index - The local variable index to load from.
        Returns:
        the inserted instruction
      • insertIStore

        public IStoreInstruction insertIStore​(int offset,
                                              int index)
        Inserts an ISTORE instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        index - The local variable index to store into.
        Returns:
        the inserted instruction
      • insertLLoad

        public LLoadInstruction insertLLoad​(int offset,
                                            int index)
        Inserts an LLOAD instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        index - The local variable index to load from.
        Returns:
        the inserted instruction
      • insertLStore

        public LStoreInstruction insertLStore​(int offset,
                                              int index)
        Inserts an LSTORE instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        index - The local variable index to store into.
        Returns:
        the inserted instruction
      • insertFLoad

        public FLoadInstruction insertFLoad​(int offset,
                                            int index)
        Inserts an FLOAD instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        index - The local variable index to load from.
        Returns:
        the inserted instruction
      • insertFStore

        public FStoreInstruction insertFStore​(int offset,
                                              int index)
        Inserts an FSTORE instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        index - The local variable index to store into.
        Returns:
        the inserted instruction
      • insertDLoad

        public DLoadInstruction insertDLoad​(int offset,
                                            int index)
        Inserts a DLOAD instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        index - The local variable index to load from.
        Returns:
        the inserted instruction
      • insertDStore

        public DStoreInstruction insertDStore​(int offset,
                                              int index)
        Inserts a DSTORE instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        index - The local variable index to store into.
        Returns:
        the inserted instruction
      • insertIInc

        public IIncInstruction insertIInc​(int offset,
                                          int varIndex,
                                          int increment)
        Inserts an IINC instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        varIndex - The local variable index to increment.
        increment - The constant by which to increment the variable.
        Returns:
        the inserted instruction
      • insertGoto

        public GotoInstruction insertGoto​(int offset,
                                          short branchOffset)
        Inserts a GOTO instruction at the specified bytecode offset with a signed short branch offset.
        Parameters:
        offset - The bytecode offset to insert the GOTO instruction at.
        branchOffset - The signed short branch offset relative to the GOTO instruction.
        Returns:
        the inserted instruction
      • insertGotoW

        public GotoInstruction insertGotoW​(int offset,
                                           int branchOffset)
        Inserts a GOTO_W instruction at the specified bytecode offset with a 32-bit branch offset.
        Parameters:
        offset - The bytecode offset to insert the GOTO_W instruction at.
        branchOffset - The signed 32-bit branch offset relative to the GOTO_W instruction.
        Returns:
        the inserted instruction
        Throws:
        IllegalArgumentException - If the specified offset is invalid.
      • insertNew

        public NewObjectInstruction insertNew​(int offset,
                                              int classRefIndex)
        Inserts a NEW instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        classRefIndex - The index into the constant pool for the class reference.
        Returns:
        the inserted instruction
      • insertLDC

        public LdcInstruction insertLDC​(int offset,
                                        int constantPoolIndex)
        Inserts an LDC instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        constantPoolIndex - The index into the constant pool for the constant.
        Returns:
        the inserted instruction
      • insertLDCW

        public LdcWInstruction insertLDCW​(int offset,
                                          int constantPoolIndex)
        Inserts an LDC_W instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        constantPoolIndex - The 2-byte index into the constant pool for the constant.
        Returns:
        the inserted instruction
      • insertTableSwitch

        public TableSwitchInstruction insertTableSwitch​(int offset,
                                                        int padding,
                                                        int defaultOffset,
                                                        int low,
                                                        int high,
                                                        Map<Integer,​Integer> jumpOffsets)
        Inserts a TABLESWITCH instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        padding - The number of padding bytes (0-3 for 4-byte alignment).
        defaultOffset - The branch target offset if no key matches.
        low - The lowest key value.
        high - The highest key value.
        jumpOffsets - The map of key to branch target offsets.
        Returns:
        The created TableSwitchInstruction.
      • insertLookupSwitch

        public LookupSwitchInstruction insertLookupSwitch​(int offset,
                                                          int padding,
                                                          int defaultOffset,
                                                          int npairs,
                                                          Map<Integer,​Integer> matchOffsets)
        Inserts a LOOKUPSWITCH instruction at the specified bytecode offset.
        Parameters:
        offset - The bytecode offset to insert the instruction at.
        padding - The number of padding bytes (0-3 for 4-byte alignment).
        defaultOffset - The default branch target offset.
        npairs - The number of key-offset pairs.
        matchOffsets - The map of keys to branch target offsets.
        Returns:
        The created LookupSwitchInstruction.
      • appendInstruction

        public void appendInstruction​(Instruction newInstr)
        Appends a new instruction to the end of the bytecode.
        Parameters:
        newInstr - The Instruction to append.
      • accept

        public void accept​(AbstractBytecodeVisitor visitor)
        Accepts a BytecodeVisitor to traverse and operate on the instructions.
        Parameters:
        visitor - The BytecodeVisitor implementation.
      • reload

        public void reload()
        Re-reads this writer's instruction list from the underlying CodeAttribute.