ExecutionDataAccess.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 * $Id: $
   12 *******************************************************************************/
   13package org.jacoco.core.runtime;
   14
   15import java.util.ListIterator;
   16
   17import org.jacoco.core.data.ExecutionDataStore;
   18import org.objectweb.asm.MethodVisitor;
   19import org.objectweb.asm.Opcodes;
   20
   21/**
   22 * This class implements the access from instrumented classes to execution data
   23 * storage in the runtime. Instead of directly referencing JaCoCo implementation
   24 * classes the access is decoupled through a JRE API. This avoids dependencies
   25 * from instrumented classes on JaCoCo APIs.
   26 * 
   27 * The JRE interface method used is {@link Object#equals(Object)} where the
   28 * passed argument is an {@link Object} array containing the class id (
   29 * {@link Long}), the class vm name ({@link String}) and the probe count (
   30 * {@link Integer}). After the method call the probe array instance is stored in
   31 * the first slot of the {@link Object} array.
   32 * 
   33 * @author Marc R. Hoffmann
   34 * @version $Revision: $
   35 */
   36class ExecutionDataAccess {
   37
   38    private final ExecutionDataStore store;
   39
   40    ExecutionDataAccess(final ExecutionDataStore store) {
   41        this.store = store;
   42    }
   43
   44    /**
   45     * Retrieves the execution probe array for a given class. The passed
   46     * {@link Object} array instance is used for parameters and the return value
   47     * as follows. Call parameters:
   48     * 
   49     * <ul>
   50     * <li>args[0]: class id ({@link Long})
   51     * <li>args[1]: vm class name ({@link String})
   52     * <li>args[2]: probe count ({@link Integer})
   53     * </ul>
   54     * 
   55     * Return value:
   56     * 
   57     * <ul>
   58     * <li>args[0]: probe array (<code>boolean[]</code>)
   59     * </ul>
   60     * 
   61     * @param args
   62     *            parameter array of length 3
   63     */
   64    public void getExecutionData(final Object[] args) {
   65        final Long classid = (Long) args[0];
   66        final String name = (String) args[1];
   67        final int probecount = ((Integer) args[2]).intValue();
   68        synchronized (store) {
   69            args[0] = store.getData(classid, name, probecount);
   70        }
   71    }
   72
   73    /**
   74     * Generates code that creates the argument array for the
   75     * <code>getExecutionData()</code> method. The array instance is left on the
   76     * operand stack. The generated code requires a stack size of 5.
   77     * 
   78     * @param classid
   79     *            class identifier
   80     * @param classname
   81     *            VM class name
   82     * @param probecount
   83     *            probe count for this class
   84     * @param mv
   85     *            visitor to emit generated code
   86     */
   87    public static void generateArgumentArray(final long classid,
   88            final String classname, final int probecount, final MethodVisitor mv) {
   89        mv.visitInsn(Opcodes.ICONST_3);
   90        mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
   91
   92        // Class Id:
   93        mv.visitInsn(Opcodes.DUP);
   94        mv.visitInsn(Opcodes.ICONST_0);
   95        mv.visitLdcInsn(Long.valueOf(classid));
   96        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf",
   97                "(J)Ljava/lang/Long;");
   98        mv.visitInsn(Opcodes.AASTORE);
   99
  100        // Class Name:
  101        mv.visitInsn(Opcodes.DUP);
  102        mv.visitInsn(Opcodes.ICONST_1);
  103        mv.visitLdcInsn(classname);
  104        mv.visitInsn(Opcodes.AASTORE);
  105
  106        // Probe Count:
  107        mv.visitInsn(Opcodes.DUP);
  108        mv.visitInsn(Opcodes.ICONST_2);
  109        mv.visitLdcInsn(Integer.valueOf(probecount));
  110        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer",
  111                "valueOf", "(I)Ljava/lang/Integer;");
  112        mv.visitInsn(Opcodes.AASTORE);
  113    }
  114
  115    /**
  116     * Generates the code that calls the runtime data access through the JRE API
  117     * method {@link ListIterator#add(Object)}. The code pops a
  118     * {@link ListIterator} instance from the stack and pushes the probe array
  119     * of type <code>boolean[]</code> on the operand stack. The generated code
  120     * requires a stack size of 6.
  121     * 
  122     * @param classid
  123     * @param classname
  124     * @param probecount
  125     * @param mv
  126     */
  127    public static void generateAccessCall(final long classid,
  128            final String classname, final int probecount, final MethodVisitor mv) {
  129        // stack[0]: Ljava/util/ListIterator;
  130
  131        generateArgumentArray(classid, classname, probecount, mv);
  132
  133        // stack[1]: [Ljava/lang/Object;
  134        // stack[0]: Ljava/util/ListIterator;
  135
  136        mv.visitInsn(Opcodes.DUP_X1);
  137
  138        // stack[2]: [Ljava/lang/Object;
  139        // stack[1]: Ljava/util/ListIterator;
  140        // stack[0]: [Ljava/lang/Object;
  141
  142        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "equals",
  143                "(Ljava/lang/Object;)Z");
  144        mv.visitInsn(Opcodes.POP);
  145
  146        // stack[0]: [Ljava/lang/Object;
  147
  148        mv.visitInsn(Opcodes.ICONST_0);
  149        mv.visitInsn(Opcodes.AALOAD);
  150
  151        // stack[0]: [Z
  152
  153        mv.visitTypeInsn(Opcodes.CHECKCAST, "[Z");
  154    }
  155
  156    /**
  157     * In violation of the regular semantic of {@link Object#equals(Object)}
  158     * this implementation is used as the interface to the execution data store.
  159     */
  160    @Override
  161    public boolean equals(final Object o) {
  162        getExecutionData((Object[]) o);
  163        return false;
  164    }
  165
  166}