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 Set<Label> targetLabels;
   37
   38    private int blockCount;
   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 access
   46     *            the method's access flags
   47     * @param name
   48     *            the method's name.
   49     * @param desc
   50     *            the method's descriptor
   51     * @param signature
   52     *            the method's signature. May be <tt>null</tt>.
   53     * @param exceptions
   54     *            the internal names of the method's exception classes. May be
   55     *            <tt>null</tt>.
   56     */
   57    public BlockMethodAdapter(IBlockMethodVisitor blockVisitor,
   58            final int access, final String name, final String desc,
   59            final String signature, final String[] exceptions) {
   60        super(access, name, desc, signature, exceptions);
   61        this.blockVisitor = blockVisitor;
   62        this.targetLabels = new HashSet<Label>();
   63        this.blockCount = 0;
   64    }
   65
   66    /**
   67     * Returns the number of blocks found in the method. A valid return value
   68     * can only be expected after {@link #visitEnd()} has been called.
   69     * 
   70     * @return number of block in the method
   71     */
   72    public int getBlockCount() {
   73        return blockCount;
   74    }
   75
   76    // === MethodVisitor ===
   77
   78    @Override
   79    public void visitJumpInsn(int opcode, Label label) {
   80        targetLabels.add(label);
   81        super.visitJumpInsn(opcode, label);
   82    }
   83
   84    @Override
   85    public void visitTableSwitchInsn(int min, int max, Label dflt,
   86            Label[] labels) {
   87        targetLabels.add(dflt);
   88        for (final Label l : labels) {
   89            targetLabels.add(l);
   90        }
   91        super.visitTableSwitchInsn(min, max, dflt, labels);
   92    }
   93
   94    @Override
   95    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
   96        targetLabels.add(dflt);
   97        for (final Label l : labels) {
   98            targetLabels.add(l);
   99        }
  100        super.visitLookupSwitchInsn(dflt, keys, labels);
  101    }
  102
  103    @Override
  104    public void visitTryCatchBlock(Label start, Label end, Label handler,
  105            String type) {
  106        targetLabels.add(start);
  107        targetLabels.add(end);
  108        targetLabels.add(handler);
  109        super.visitTryCatchBlock(start, end, handler, type);
  110    }
  111
  112    @Override
  113    public void visitEnd() {
  114        accept(new BlockFinder());
  115    }
  116
  117    private final class BlockFinder extends MethodAdapter {
  118
  119        private boolean blockStarted;
  120
  121        public BlockFinder() {
  122            super(blockVisitor);
  123            blockStarted = false;
  124        }
  125
  126        private void onBlockEndBeforeJump() {
  127            if (blockStarted) {
  128                blockVisitor.visitBlockEndBeforeJump(blockCount);
  129            }
  130        }
  131
  132        private void onBlockEnd() {
  133            if (blockStarted) {
  134                blockVisitor.visitBlockEnd(blockCount);
  135                blockCount++;
  136                blockStarted = false;
  137            }
  138        }
  139
  140        @Override
  141        public void visitLabel(Label label) {
  142            if (targetLabels.contains(label)) {
  143                onBlockEndBeforeJump();
  144                onBlockEnd();
  145            }
  146            super.visitLabel(label);
  147        }
  148
  149        @Override
  150        public void visitJumpInsn(int opcode, Label label) {
  151            blockStarted = true;
  152            onBlockEndBeforeJump();
  153            super.visitJumpInsn(opcode, label);
  154            onBlockEnd();
  155        }
  156
  157        @Override
  158        public void visitInsn(int opcode) {
  159            blockStarted = true;
  160            switch (opcode) {
  161            case Opcodes.RETURN:
  162            case Opcodes.IRETURN:
  163            case Opcodes.FRETURN:
  164            case Opcodes.LRETURN:
  165            case Opcodes.DRETURN:
  166            case Opcodes.ARETURN:
  167            case Opcodes.ATHROW:
  168                onBlockEndBeforeJump();
  169                super.visitInsn(opcode);
  170                onBlockEnd();
  171                break;
  172            default:
  173                super.visitInsn(opcode);
  174                break;
  175            }
  176        }
  177
  178        @Override
  179        public void visitTableSwitchInsn(int min, int max, Label dflt,
  180                Label[] labels) {
  181            blockStarted = true;
  182            onBlockEndBeforeJump();
  183            super.visitTableSwitchInsn(min, max, dflt, labels);
  184            onBlockEnd();
  185        }
  186
  187        @Override
  188        public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
  189            blockStarted = true;
  190            onBlockEndBeforeJump();
  191            super.visitLookupSwitchInsn(dflt, keys, labels);
  192            onBlockEnd();
  193        }
  194
  195        @Override
  196        public void visitFieldInsn(int opcode, String owner, String name,
  197                String desc) {
  198            blockStarted = true;
  199            super.visitFieldInsn(opcode, owner, name, desc);
  200        }
  201
  202        @Override
  203        public void visitIincInsn(int var, int increment) {
  204            blockStarted = true;
  205            super.visitIincInsn(var, increment);
  206        }
  207
  208        @Override
  209        public void visitIntInsn(int opcode, int operand) {
  210            blockStarted = true;
  211            super.visitIntInsn(opcode, operand);
  212        }
  213
  214        @Override
  215        public void visitLdcInsn(Object cst) {
  216            blockStarted = true;
  217            super.visitLdcInsn(cst);
  218        }
  219
  220        @Override
  221        public void visitMethodInsn(int opcode, String owner, String name,
  222                String desc) {
  223            blockStarted = true;
  224            super.visitMethodInsn(opcode, owner, name, desc);
  225        }
  226
  227        @Override
  228        public void visitMultiANewArrayInsn(String desc, int dims) {
  229            blockStarted = true;
  230            super.visitMultiANewArrayInsn(desc, dims);
  231        }
  232
  233        @Override
  234        public void visitTypeInsn(int opcode, String type) {
  235            blockStarted = true;
  236            super.visitTypeInsn(opcode, type);
  237        }
  238
  239        @Override
  240        public void visitVarInsn(int opcode, int var) {
  241            blockStarted = true;
  242            super.visitVarInsn(opcode, var);
  243        }
  244
  245    }
  246
  247}