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