BlockMethodAdapter.java

    1/*******************************************************************************
    2 * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
    3 * All rights reserved. This program and the accompanying materials
    4 * are made available under the terms of the Eclipse Public License v1.0
    5 * which accompanies this distribution, and is available at
    6 * http://www.eclipse.org/legal/epl-v10.html
    7 *
    8 * Contributors:
    9 *    Marc R. Hoffmann - initial API and implementation
   10 *    
   11 * $Id: $
   12 *******************************************************************************/
   13package org.jacoco.core.instr;
   14
   15import java.util.HashSet;
   16import java.util.Set;
   17
   18import org.objectweb.asm.Label;
   19import org.objectweb.asm.MethodAdapter;
   20import org.objectweb.asm.Opcodes;
   21import org.objectweb.asm.tree.MethodNode;
   22
   23/**
   24 * A method visitor that determines block boundaries and reports them to the
   25 * wrapped {@link IBlockMethodVisitor}. The implementation first buffers the
   26 * content of the method to extract all control flow target labels. At the end
   27 * of the method it flushes the content to the {@link IBlockMethodVisitor}.
   28 * 
   29 * @author Marc R. Hoffmann
   30 * @version $Revision: $
   31 */
   32public final class BlockMethodAdapter extends MethodNode {
   33
   34    private final IBlockMethodVisitor blockVisitor;
   35
   36    private final IProbeIdGenerator idGenerator;
   37
   38    private final Set<Label> targetLabels;
   39
   40    /**
   41     * Create a new adapter for the given block visitor.
   42     * 
   43     * @param blockVisitor
   44     *            visitor to report block boundaries to
   45     * @param idGenerator
   46     *            generator for probe ids
   47     * @param access
   48     *            the method's access flags
   49     * @param name
   50     *            the method's name.
   51     * @param desc
   52     *            the method's descriptor
   53     * @param signature
   54     *            the method's signature. May be <tt>null</tt>.
   55     * @param exceptions
   56     *            the internal names of the method's exception classes. May be
   57     *            <tt>null</tt>.
   58     */
   59    public BlockMethodAdapter(final IBlockMethodVisitor blockVisitor,
   60            final IProbeIdGenerator idGenerator, final int access,
   61            final String name, final String desc, final String signature,
   62            final String[] exceptions) {
   63        super(access, name, desc, signature, exceptions);
   64        this.blockVisitor = blockVisitor;
   65        this.idGenerator = idGenerator;
   66        this.targetLabels = new HashSet<Label>();
   67    }
   68
   69    // === MethodVisitor ===
   70
   71    @Override
   72    public void visitJumpInsn(final int opcode, final Label label) {
   73        targetLabels.add(label);
   74        super.visitJumpInsn(opcode, label);
   75    }
   76
   77    @Override
   78    public void visitTableSwitchInsn(final int min, final int max,
   79            final Label dflt, final Label[] labels) {
   80        targetLabels.add(dflt);
   81        for (final Label l : labels) {
   82            targetLabels.add(l);
   83        }
   84        super.visitTableSwitchInsn(min, max, dflt, labels);
   85    }
   86
   87    @Override
   88    public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
   89            final Label[] labels) {
   90        targetLabels.add(dflt);
   91        for (final Label l : labels) {
   92            targetLabels.add(l);
   93        }
   94        super.visitLookupSwitchInsn(dflt, keys, labels);
   95    }
   96
   97    @Override
   98    public void visitTryCatchBlock(final Label start, final Label end,
   99            final Label handler, final String type) {
  100        targetLabels.add(start);
  101        targetLabels.add(end);
  102        targetLabels.add(handler);
  103        super.visitTryCatchBlock(start, end, handler, type);
  104    }
  105
  106    @Override
  107    public void visitEnd() {
  108        accept(new BlockFinder());
  109    }
  110
  111    private final class BlockFinder extends MethodAdapter {
  112
  113        private boolean blockStarted;
  114
  115        private int id;
  116
  117        public BlockFinder() {
  118            super(blockVisitor);
  119            blockStarted = false;
  120        }
  121
  122        private void onBlockEndBeforeJump() {
  123            if (blockStarted) {
  124                id = idGenerator.nextId();
  125                blockVisitor.visitBlockEndBeforeJump(id);
  126            }
  127        }
  128
  129        private void onBlockEnd() {
  130            if (blockStarted) {
  131                blockVisitor.visitBlockEnd(id);
  132                blockStarted = false;
  133            }
  134        }
  135
  136        @Override
  137        public void visitLabel(final Label label) {
  138            if (targetLabels.contains(label)) {
  139                onBlockEndBeforeJump();
  140                onBlockEnd();
  141            }
  142            super.visitLabel(label);
  143        }
  144
  145        @Override
  146        public void visitJumpInsn(final int opcode, final Label label) {
  147            blockStarted = true;
  148            onBlockEndBeforeJump();
  149            super.visitJumpInsn(opcode, label);
  150            onBlockEnd();
  151        }
  152
  153        @Override
  154        public void visitInsn(final int opcode) {
  155            blockStarted = true;
  156            switch (opcode) {
  157            case Opcodes.RETURN:
  158            case Opcodes.IRETURN:
  159            case Opcodes.FRETURN:
  160            case Opcodes.LRETURN:
  161            case Opcodes.DRETURN:
  162            case Opcodes.ARETURN:
  163            case Opcodes.ATHROW:
  164                onBlockEndBeforeJump();
  165                super.visitInsn(opcode);
  166                onBlockEnd();
  167                break;
  168            default:
  169                super.visitInsn(opcode);
  170                break;
  171            }
  172        }
  173
  174        @Override
  175        public void visitTableSwitchInsn(final int min, final int max,
  176                final Label dflt, final Label[] labels) {
  177            blockStarted = true;
  178            onBlockEndBeforeJump();
  179            super.visitTableSwitchInsn(min, max, dflt, labels);
  180            onBlockEnd();
  181        }
  182
  183        @Override
  184        public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
  185                final Label[] labels) {
  186            blockStarted = true;
  187            onBlockEndBeforeJump();
  188            super.visitLookupSwitchInsn(dflt, keys, labels);
  189            onBlockEnd();
  190        }
  191
  192        @Override
  193        public void visitFieldInsn(final int opcode, final String owner,
  194                final String name, final String desc) {
  195            blockStarted = true;
  196            super.visitFieldInsn(opcode, owner, name, desc);
  197        }
  198
  199        @Override
  200        public void visitIincInsn(final int var, final int increment) {
  201            blockStarted = true;
  202            super.visitIincInsn(var, increment);
  203        }
  204
  205        @Override
  206        public void visitIntInsn(final int opcode, final int operand) {
  207            blockStarted = true;
  208            super.visitIntInsn(opcode, operand);
  209        }
  210
  211        @Override
  212        public void visitLdcInsn(final Object cst) {
  213            blockStarted = true;
  214            super.visitLdcInsn(cst);
  215        }
  216
  217        @Override
  218        public void visitMethodInsn(final int opcode, final String owner,
  219                final String name, final String desc) {
  220            blockStarted = true;
  221            super.visitMethodInsn(opcode, owner, name, desc);
  222        }
  223
  224        @Override
  225        public void visitMultiANewArrayInsn(final String desc, final int dims) {
  226            blockStarted = true;
  227            super.visitMultiANewArrayInsn(desc, dims);
  228        }
  229
  230        @Override
  231        public void visitTypeInsn(final int opcode, final String type) {
  232            blockStarted = true;
  233            super.visitTypeInsn(opcode, type);
  234        }
  235
  236        @Override
  237        public void visitVarInsn(final int opcode, final int var) {
  238            blockStarted = true;
  239            super.visitVarInsn(opcode, var);
  240        }
  241
  242    }
  243
  244}