BlockMethodAdapter.java

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