Package org.codehaus.groovy.classgen.asm

# Class PeepholeOptimizingMethodVisitor

java.lang.Object
org.objectweb.asm.MethodVisitor
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(boolean)`), 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(org.objectweb.asm.MethodVisitor, int)` on the delegate so they are not re-buffered by this visitor.

Since:
  6.0.0
See Also:
  - `PeepholeOptimizingClassVisitor`
  - `OperandStack`
  - `BytecodeHelper.pushConstant(MethodVisitor, int)`

- ## Field Summary

  ### Fields inherited from class org.objectweb.asm.MethodVisitor

  `api, mv`

- ## Constructor Summary

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

- ## Method Summary

  Modifier and Type
  Method
  Description
  `static boolean`
  `printTraceBytecode(org.objectweb.asm.MethodVisitor visitor, PrintWriter out)`
  Walks `visitor` and any nested `PeepholeOptimizingMethodVisitor` layers to find a `TraceMethodVisitor`, then prints that visitor's recorded instruction text to `out`.
  `void`
  `visitAttribute(org.objectweb.asm.Attribute attribute)`
  `void`
  `visitEnd()`
  `void`
  `visitFieldInsn(int opcode, String owner, String name, String descriptor)`
  `void`
  `visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack)`
  `void`
  `visitIincInsn(int varIndex, int increment)`
  `void`
  `visitInsn(int opcode)`
  `org.objectweb.asm.AnnotationVisitor`
  `visitInsnAnnotation(int typeRef, org.objectweb.asm.TypePath typePath, String descriptor, boolean visible)`
  `void`
  `visitIntInsn(int opcode, int operand)`
  `void`
  `visitInvokeDynamicInsn(String name, String descriptor, org.objectweb.asm.Handle bootstrapMethodHandle, Object... bootstrapMethodArguments)`
  `void`
  `visitJumpInsn(int opcode, org.objectweb.asm.Label label)`
  `void`
  `visitLabel(org.objectweb.asm.Label label)`
  `void`
  `visitLdcInsn(Object value)`
  `void`
  `visitLineNumber(int line, org.objectweb.asm.Label start)`
  `void`
  `visitLocalVariable(String name, String descriptor, String signature, org.objectweb.asm.Label start, org.objectweb.asm.Label end, int index)`
  `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)`
  `void`
  `visitLookupSwitchInsn(org.objectweb.asm.Label dflt, int[] keys, org.objectweb.asm.Label[] labels)`
  `void`
  `visitMaxs(int maxStack, int maxLocals)`
  `void`
  `visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface)`
  `void`
  `visitMultiANewArrayInsn(String descriptor, int dims)`
  `void`
  `visitTableSwitchInsn(int min, int max, org.objectweb.asm.Label dflt, org.objectweb.asm.Label... labels)`
  `org.objectweb.asm.AnnotationVisitor`
  `visitTryCatchAnnotation(int typeRef, org.objectweb.asm.TypePath typePath, String descriptor, boolean visible)`
  `void`
  `visitTryCatchBlock(org.objectweb.asm.Label start, org.objectweb.asm.Label end, org.objectweb.asm.Label handler, String type)`
  `void`
  `visitTypeInsn(int opcode, String descriptor)`
  `void`
  `visitVarInsn(int opcode, int varIndex)`
  `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.

  ### Methods inherited from class org.objectweb.asm.MethodVisitor

  `getDelegate, visitAnnotableParameterCount, visitAnnotation, visitAnnotationDefault, visitCode, visitMethodInsn, visitParameter, visitParameterAnnotation, visitTypeAnnotation`

  ### Methods inherited from class java.lang.Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Constructor Details

  - ### PeepholeOptimizingMethodVisitor

    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 `MethodWriter` or `TraceMethodVisitor`)

- ## Method Details

  - ### wrap

    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 `ClassVisitor.visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])` may return `null` to skip a method body. Used by `PeepholeOptimizingClassVisitor.visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])` 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`

  - ### printTraceBytecode

    public static boolean printTraceBytecode(org.objectweb.asm.MethodVisitor visitor, PrintWriter out)
    Walks `visitor` and any nested `PeepholeOptimizingMethodVisitor` layers to find a `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 `TraceMethodVisitor` was found and printed; `false` if none was present in the chain

  - ### visitAttribute

    public void visitAttribute(org.objectweb.asm.Attribute attribute)
    Overrides:
      `visitAttribute` in class `org.objectweb.asm.MethodVisitor`

  - ### visitFrame

    public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack)
    Overrides:
      `visitFrame` in class `org.objectweb.asm.MethodVisitor`

  - ### visitInsn

    public void visitInsn(int opcode)
    Overrides:
      `visitInsn` in class `org.objectweb.asm.MethodVisitor`

  - ### visitIntInsn

    public void visitIntInsn(int opcode, int operand)
    Overrides:
      `visitIntInsn` in class `org.objectweb.asm.MethodVisitor`

  - ### visitVarInsn

    public void visitVarInsn(int opcode, int varIndex)
    Overrides:
      `visitVarInsn` in class `org.objectweb.asm.MethodVisitor`

  - ### visitTypeInsn

    public void visitTypeInsn(int opcode, String descriptor)
    Overrides:
      `visitTypeInsn` in class `org.objectweb.asm.MethodVisitor`

  - ### visitFieldInsn

    public void visitFieldInsn(int opcode, String owner, String name, String descriptor)
    Overrides:
      `visitFieldInsn` in class `org.objectweb.asm.MethodVisitor`

  - ### visitMethodInsn

    public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface)
    Overrides:
      `visitMethodInsn` in class `org.objectweb.asm.MethodVisitor`

  - ### visitInvokeDynamicInsn

    public void visitInvokeDynamicInsn(String name, String descriptor, org.objectweb.asm.Handle bootstrapMethodHandle, Object... bootstrapMethodArguments)
    Overrides:
      `visitInvokeDynamicInsn` in class `org.objectweb.asm.MethodVisitor`

  - ### visitJumpInsn

    public void visitJumpInsn(int opcode, org.objectweb.asm.Label label)
    Overrides:
      `visitJumpInsn` in class `org.objectweb.asm.MethodVisitor`

  - ### visitLabel

    public void visitLabel(org.objectweb.asm.Label label)
    Overrides:
      `visitLabel` in class `org.objectweb.asm.MethodVisitor`

  - ### visitLdcInsn

    public void visitLdcInsn(Object value)
    Overrides:
      `visitLdcInsn` in class `org.objectweb.asm.MethodVisitor`

  - ### visitIincInsn

    public void visitIincInsn(int varIndex, int increment)
    Overrides:
      `visitIincInsn` in class `org.objectweb.asm.MethodVisitor`

  - ### visitTableSwitchInsn

    public void visitTableSwitchInsn(int min, int max, org.objectweb.asm.Label dflt, org.objectweb.asm.Label... labels)
    Overrides:
      `visitTableSwitchInsn` in class `org.objectweb.asm.MethodVisitor`

  - ### visitLookupSwitchInsn

    public void visitLookupSwitchInsn(org.objectweb.asm.Label dflt, int[] keys, org.objectweb.asm.Label[] labels)
    Overrides:
      `visitLookupSwitchInsn` in class `org.objectweb.asm.MethodVisitor`

  - ### visitMultiANewArrayInsn

    public void visitMultiANewArrayInsn(String descriptor, int dims)
    Overrides:
      `visitMultiANewArrayInsn` in class `org.objectweb.asm.MethodVisitor`

  - ### visitInsnAnnotation

    public org.objectweb.asm.AnnotationVisitor visitInsnAnnotation(int typeRef, org.objectweb.asm.TypePath typePath, String descriptor, boolean visible)
    Overrides:
      `visitInsnAnnotation` in class `org.objectweb.asm.MethodVisitor`

  - ### visitTryCatchBlock

    public void visitTryCatchBlock(org.objectweb.asm.Label start, org.objectweb.asm.Label end, org.objectweb.asm.Label handler, String type)
    Overrides:
      `visitTryCatchBlock` in class `org.objectweb.asm.MethodVisitor`

  - ### visitTryCatchAnnotation

    public org.objectweb.asm.AnnotationVisitor visitTryCatchAnnotation(int typeRef, org.objectweb.asm.TypePath typePath, String descriptor, boolean visible)
    Overrides:
      `visitTryCatchAnnotation` in class `org.objectweb.asm.MethodVisitor`

  - ### visitLocalVariable

    public void visitLocalVariable(String name, String descriptor, String signature, org.objectweb.asm.Label start, org.objectweb.asm.Label end, int index)
    Overrides:
      `visitLocalVariable` in class `org.objectweb.asm.MethodVisitor`

  - ### visitLocalVariableAnnotation

    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)
    Overrides:
      `visitLocalVariableAnnotation` in class `org.objectweb.asm.MethodVisitor`

  - ### visitLineNumber

    public void visitLineNumber(int line, org.objectweb.asm.Label start)
    Overrides:
      `visitLineNumber` in class `org.objectweb.asm.MethodVisitor`

  - ### visitMaxs

    public void visitMaxs(int maxStack, int maxLocals)
    Overrides:
      `visitMaxs` in class `org.objectweb.asm.MethodVisitor`

  - ### visitEnd

    public void visitEnd()
    Overrides:
      `visitEnd` in class `org.objectweb.asm.MethodVisitor`