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 narrowingLDC/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 / nullICONST_0; IF_ICMP*IF*; ACONST_NULL; IF_ACMP*IFNULL/IFNONNULL.
  • DUP + store + popDUP/DUP2; store; matching pop becomes a plain store; bare DUP/DUP2 with a matching pop is eliminated.
  • Box/unbox cancellation and conversionWrapper.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 foldingGETSTATIC 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:
  • 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