Package: org.codehaus.groovy.classgen.asm

## [Java] Class PeepholeOptimizingMethodVisitor

- org.codehaus.groovy.classgen.asm.PeepholeOptimizingMethodVisitor

-     public final class PeepholeOptimizingMethodVisitor
      extends org.objectweb.asm.MethodVisitor

  Single-pass, stack-local bytecode compaction for methods emitted by the Groovy class generator. Inspired by Groovy++'s peephole adapters.

  Upstream writers such as OperandStack and BytecodeHelper may emit a uniform, easy-to-generate form (for example `visitLdcInsn` for every integer, or box immediately after a primitive producer). This visitor rewrites those sequences, within a single basic-block window, into the densest equivalent JVM opcodes without a second compilation pass or a full data-flow analysis.

  ## Model

  At most one pending load (constant, variable load, standalone `CHECKCAST`, or `Boolean.TRUE`/`FALSE`), at most one pending box (`Wrapper.valueOf` or DefaultTypeTransformation.box), and at most one pending `DUP`/`DUP2` (optionally followed by a buffered store) are held at a time. Pending state is flushed to the delegate before any non-local boundary so control flow, frames, and debug metadata stay correct:
  - labels, jump targets, table/lookup switches
  - stack map frames
  - line numbers, local-variable tables, and type annotations
  - method and `invokedynamic` calls (except matched box/unbox pairs)
  - `visitMaxs` / `visitEnd`

  ## Rewrites

  - Constant narrowing — `LDC`/push forms become `ICONST_*`, `BIPUSH`, `SIPUSH`, `LCONST_*`, `FCONST_*`, `DCONST_*`, or `ACONST_NULL` when legal. Signed floating-point zeros (`-0.0f`/`-0.0d`) are preserved via raw-bit comparison (GROOVY-9797).
  - Dead loads — a buffered load or constant followed by a matching `POP`/`POP2`, or a void `RETURN`, is dropped when the load is pure (no attached `CHECKCAST`). A buffered `IINC` paired with `ILOAD` is retained as a side effect when the loaded value itself is discarded.
  - CHECKCAST — may attach to a pending `ALOAD` or reference constant so the cast is emitted immediately after the load on flush. When the cast result is discarded (`POP` or void `RETURN`), the cast is always kept so a possible `ClassCastException` remains observable — matching Java and the void-return path. Pure loads without a cast still collapse with the pop. Standalone casts of a value already on the stack are likewise preserved before pop/return, with `POP` inserted before void `RETURN` so the frame stays verifiable.
  - Compare-to-zero / null — `ICONST_0`; `IF_ICMP*` → `IF*`; `ACONST_NULL`; `IF_ACMP*` → `IFNULL`/`IFNONNULL`.
  - DUP + store + pop — `DUP`/`DUP2`; store; matching pop becomes a plain store; bare `DUP`/`DUP2` with a matching pop is eliminated.
  - Box/unbox cancellation and conversion — `Wrapper.valueOf(p)` / `DefaultTypeTransformation.box(p)` followed by a same-type unbox (`Wrapper.xxxValue()` or `DefaultTypeTransformation.xxxUnbox`) cancel to a no-op (the primitive remains on the stack). Cross-convention pairs of the same primitive type are included (for example `DTT.box(I)` then `Integer.intValue()`). When the unbox targets a different numeric primitive, the pair is rewritten to the matching JVM conversion (`I2L`, `L2I`, `I2F`, …), including cross-wrapper forms such as `Integer.valueOf` then `Long.longValue`: after a pending box the runtime value is known to be a properly boxed primitive, so a wrong-owner unbox cannot express a conditional CCE path (and real classgen uses `I2L` for `long l = intExpr` directly). Boolean is excluded from conversion rewrites (only same-type cancel applies). A boxed value discarded by `POP`/`POP2` (or void `RETURN`) drops the box and, when the primitive producer is still in the load window, drops that producer too; otherwise it pops the original primitive (`POP2` when wide). `POP2` on a narrow boxed value is treated as two one-slot discards (box/primitive plus the slot underneath).
  - Boolean constant folding — `GETSTATIC Boolean.TRUE/FALSE` followed by `booleanValue()` or `DefaultTypeTransformation.booleanUnbox` becomes `ICONST_1`/`ICONST_0`.
  - Big number lowering — buffered BigDecimal/BigInteger constants become `new Type(String)` construction on flush.

  ## Installation

  Prefer PeepholeOptimizingClassVisitor (wired from WriterController) so every method is covered. Use wrap(MethodVisitor) only when constructing a method visitor outside that chain (for example unit tests). Integer constants are re-emitted through BytecodeHelper.pushConstant on the delegate so they are not re-buffered by this visitor.
  See Also:
    PeepholeOptimizingClassVisitor
    OperandStack
    BytecodeHelper.pushConstant
  Since:
    6.0.0

- - ### Constructor Summary

    Constructors
    Constructor and description
    `PeepholeOptimizingMethodVisitor(org.objectweb.asm.MethodVisitor delegate)` Creates a peephole visitor that forwards compacted instructions to `delegate`.

  - ### Methods Summary

    Methods
    Type Params | Return Type | Name and description
    --- | --- | ---
    `` | `public static boolean` | `printTraceBytecode(org.objectweb.asm.MethodVisitor visitor, PrintWriter out)` Walks `visitor` and any nested PeepholeOptimizingMethodVisitor layers to find a org.objectweb.asm.util.TraceMethodVisitor, then prints that visitor's recorded instruction text to `out`.
    `` | `public void` | `visitAttribute(org.objectweb.asm.Attribute attribute)`
    `` | `public void` | `visitEnd()`
    `` | `public void` | `visitFieldInsn(int opcode, String owner, String name, String descriptor)`
    `` | `public void` | `visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack)`
    `` | `public void` | `visitIincInsn(int varIndex, int increment)`
    `` | `public void` | `visitInsn(int opcode)`
    `` | `public org.objectweb.asm.AnnotationVisitor` | `visitInsnAnnotation(int typeRef, org.objectweb.asm.TypePath typePath, String descriptor, boolean visible)`
    `` | `public void` | `visitIntInsn(int opcode, int operand)`
    `` | `public void` | `visitInvokeDynamicInsn(String name, String descriptor, org.objectweb.asm.Handle bootstrapMethodHandle, Object bootstrapMethodArguments)`
    `` | `public void` | `visitJumpInsn(int opcode, org.objectweb.asm.Label label)`
    `` | `public void` | `visitLabel(org.objectweb.asm.Label label)`
    `` | `public void` | `visitLdcInsn(Object value)`
    `` | `public void` | `visitLineNumber(int line, org.objectweb.asm.Label start)`
    `` | `public void` | `visitLocalVariable(String name, String descriptor, String signature, org.objectweb.asm.Label start, org.objectweb.asm.Label end, int index)`
    `` | `public org.objectweb.asm.AnnotationVisitor` | `visitLocalVariableAnnotation(int typeRef, org.objectweb.asm.TypePath typePath, org.objectweb.asm.Label[] start, org.objectweb.asm.Label[] end, int[] index, String descriptor, boolean visible)`
    `` | `public void` | `visitLookupSwitchInsn(org.objectweb.asm.Label dflt, int[] keys, org.objectweb.asm.Label[] labels)`
    `` | `public void` | `visitMaxs(int maxStack, int maxLocals)`
    `` | `public void` | `visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface)`
    `` | `public void` | `visitMultiANewArrayInsn(String descriptor, int dims)`
    `` | `public void` | `visitTableSwitchInsn(int min, int max, org.objectweb.asm.Label dflt, org.objectweb.asm.Label labels)`
    `` | `public org.objectweb.asm.AnnotationVisitor` | `visitTryCatchAnnotation(int typeRef, org.objectweb.asm.TypePath typePath, String descriptor, boolean visible)`
    `` | `public void` | `visitTryCatchBlock(org.objectweb.asm.Label start, org.objectweb.asm.Label end, org.objectweb.asm.Label handler, String type)`
    `` | `public void` | `visitTypeInsn(int opcode, String descriptor)`
    `` | `public void` | `visitVarInsn(int opcode, int varIndex)`
    `` | `public static org.objectweb.asm.MethodVisitor` | `wrap(org.objectweb.asm.MethodVisitor delegate)` Idempotent factory: returns `delegate` unchanged when it is `null` or already a PeepholeOptimizingMethodVisitor, otherwise wraps it.

  - ### Inherited Methods Summary

    Inherited Methods
    Methods inherited from class | Name
    --- | ---
    `class org.objectweb.asm.MethodVisitor` | `org.objectweb.asm.MethodVisitor#equals(java.lang.Object), org.objectweb.asm.MethodVisitor#getClass(), org.objectweb.asm.MethodVisitor#getDelegate(), org.objectweb.asm.MethodVisitor#hashCode(), org.objectweb.asm.MethodVisitor#notify(), org.objectweb.asm.MethodVisitor#notifyAll(), org.objectweb.asm.MethodVisitor#toString(), org.objectweb.asm.MethodVisitor#visitAnnotableParameterCount(int, boolean), org.objectweb.asm.MethodVisitor#visitAnnotation(java.lang.String, boolean), org.objectweb.asm.MethodVisitor#visitAnnotationDefault(), org.objectweb.asm.MethodVisitor#visitAttribute(org.objectweb.asm.Attribute), org.objectweb.asm.MethodVisitor#visitCode(), org.objectweb.asm.MethodVisitor#visitEnd(), org.objectweb.asm.MethodVisitor#visitFieldInsn(int, java.lang.String, java.lang.String, java.lang.String), org.objectweb.asm.MethodVisitor#visitFrame(int, int, [Ljava.lang.Object;, int, [Ljava.lang.Object;), org.objectweb.asm.MethodVisitor#visitIincInsn(int, int), org.objectweb.asm.MethodVisitor#visitInsn(int), org.objectweb.asm.MethodVisitor#visitInsnAnnotation(int, org.objectweb.asm.TypePath, java.lang.String, boolean), org.objectweb.asm.MethodVisitor#visitIntInsn(int, int), org.objectweb.asm.MethodVisitor#visitInvokeDynamicInsn(java.lang.String, java.lang.String, org.objectweb.asm.Handle, [Ljava.lang.Object;), org.objectweb.asm.MethodVisitor#visitJumpInsn(int, org.objectweb.asm.Label), org.objectweb.asm.MethodVisitor#visitLabel(org.objectweb.asm.Label), org.objectweb.asm.MethodVisitor#visitLdcInsn(java.lang.Object), org.objectweb.asm.MethodVisitor#visitLineNumber(int, org.objectweb.asm.Label), org.objectweb.asm.MethodVisitor#visitLocalVariable(java.lang.String, java.lang.String, java.lang.String, org.objectweb.asm.Label, org.objectweb.asm.Label, int), org.objectweb.asm.MethodVisitor#visitLocalVariableAnnotation(int, org.objectweb.asm.TypePath, [Lorg.objectweb.asm.Label;, [Lorg.objectweb.asm.Label;, [I, java.lang.String, boolean), org.objectweb.asm.MethodVisitor#visitLookupSwitchInsn(org.objectweb.asm.Label, [I, [Lorg.objectweb.asm.Label;), org.objectweb.asm.MethodVisitor#visitMaxs(int, int), org.objectweb.asm.MethodVisitor#visitMethodInsn(int, java.lang.String, java.lang.String, java.lang.String), org.objectweb.asm.MethodVisitor#visitMethodInsn(int, java.lang.String, java.lang.String, java.lang.String, boolean), org.objectweb.asm.MethodVisitor#visitMultiANewArrayInsn(java.lang.String, int), org.objectweb.asm.MethodVisitor#visitParameter(java.lang.String, int), org.objectweb.asm.MethodVisitor#visitParameterAnnotation(int, java.lang.String, boolean), org.objectweb.asm.MethodVisitor#visitTableSwitchInsn(int, int, org.objectweb.asm.Label, [Lorg.objectweb.asm.Label;), org.objectweb.asm.MethodVisitor#visitTryCatchAnnotation(int, org.objectweb.asm.TypePath, java.lang.String, boolean), org.objectweb.asm.MethodVisitor#visitTryCatchBlock(org.objectweb.asm.Label, org.objectweb.asm.Label, org.objectweb.asm.Label, java.lang.String), org.objectweb.asm.MethodVisitor#visitTypeAnnotation(int, org.objectweb.asm.TypePath, java.lang.String, boolean), org.objectweb.asm.MethodVisitor#visitTypeInsn(int, java.lang.String), org.objectweb.asm.MethodVisitor#visitVarInsn(int, int), org.objectweb.asm.MethodVisitor#wait(), org.objectweb.asm.MethodVisitor#wait(long), org.objectweb.asm.MethodVisitor#wait(long, int)`

- - ### Constructor Detail

    - #### public PeepholeOptimizingMethodVisitor(org.objectweb.asm.MethodVisitor delegate)

      Creates a peephole visitor that forwards compacted instructions to `delegate`.

      Parameters:
        `delegate` - the next method visitor in the chain (for example a org.objectweb.asm.MethodWriter or org.objectweb.asm.util.TraceMethodVisitor)

  - ### Method Detail

    - #### public static boolean printTraceBytecode(org.objectweb.asm.MethodVisitor visitor, PrintWriter out)

      Walks `visitor` and any nested PeepholeOptimizingMethodVisitor layers to find a org.objectweb.asm.util.TraceMethodVisitor, then prints that visitor's recorded instruction text to `out`.

      AsmClassGenerator uses this when `visitMaxs` fails under classgen logging: the outer visitor is a peephole wrapper, so a direct `instanceof TraceMethodVisitor` check would miss the tracer sitting further down the chain.

      Parameters:
        `visitor` - the method visitor active during class generation (may be a peephole wrapper); `null` is treated as “not found�?
        `out` - destination for the traced bytecode listing
      Returns:
        `true` if a org.objectweb.asm.util.TraceMethodVisitor was found and printed; `false` if none was present in the chain

    - #### public void visitAttribute(org.objectweb.asm.Attribute attribute)

    - #### public void visitEnd()

    - #### public void visitFieldInsn(int opcode, String owner, String name, String descriptor)

    - #### public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack)

    - #### public void visitIincInsn(int varIndex, int increment)

    - #### public void visitInsn(int opcode)

    - #### public org.objectweb.asm.AnnotationVisitor visitInsnAnnotation(int typeRef, org.objectweb.asm.TypePath typePath, String descriptor, boolean visible)

    - #### public void visitIntInsn(int opcode, int operand)

    - #### public void visitInvokeDynamicInsn(String name, String descriptor, org.objectweb.asm.Handle bootstrapMethodHandle, Object bootstrapMethodArguments)

    - #### public void visitJumpInsn(int opcode, org.objectweb.asm.Label label)

    - #### public void visitLabel(org.objectweb.asm.Label label)

    - #### public void visitLdcInsn(Object value)

    - #### public void visitLineNumber(int line, org.objectweb.asm.Label start)

    - #### public void visitLocalVariable(String name, String descriptor, String signature, org.objectweb.asm.Label start, org.objectweb.asm.Label end, int index)

    - #### public org.objectweb.asm.AnnotationVisitor visitLocalVariableAnnotation(int typeRef, org.objectweb.asm.TypePath typePath, org.objectweb.asm.Label[] start, org.objectweb.asm.Label[] end, int[] index, String descriptor, boolean visible)

    - #### public void visitLookupSwitchInsn(org.objectweb.asm.Label dflt, int[] keys, org.objectweb.asm.Label[] labels)

    - #### public void visitMaxs(int maxStack, int maxLocals)

    - #### public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface)

    - #### public void visitMultiANewArrayInsn(String descriptor, int dims)

    - #### public void visitTableSwitchInsn(int min, int max, org.objectweb.asm.Label dflt, org.objectweb.asm.Label labels)

    - #### public org.objectweb.asm.AnnotationVisitor visitTryCatchAnnotation(int typeRef, org.objectweb.asm.TypePath typePath, String descriptor, boolean visible)

    - #### public void visitTryCatchBlock(org.objectweb.asm.Label start, org.objectweb.asm.Label end, org.objectweb.asm.Label handler, String type)

    - #### public void visitTypeInsn(int opcode, String descriptor)

    - #### public void visitVarInsn(int opcode, int varIndex)

    - #### public static org.objectweb.asm.MethodVisitor wrap(org.objectweb.asm.MethodVisitor delegate)

      Idempotent factory: returns `delegate` unchanged when it is `null` or already a PeepholeOptimizingMethodVisitor, otherwise wraps it.

      Returning `null` unchanged matches the ASM contract that org.objectweb.asm.ClassVisitor#visitMethod may return `null` to skip a method body. Used by PeepholeOptimizingClassVisitor.visitMethod so nested or repeated wrapping does not stack multiple peephole layers.

      Parameters:
        `delegate` - the visitor to wrap, or `null` to skip
      Returns:
        a peephole-optimizing method visitor, or `null` when `delegate` is `null`