org.jacoco.core.runtime.AbstractRuntimeData.java Source code

Java tutorial

Introduction

Here is the source code for org.jacoco.core.runtime.AbstractRuntimeData.java

Source

/*******************************************************************************
 * Copyright (c) 2009, 2015 Mountainminds GmbH & Co. KG and Contributors
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Marc R. Hoffmann - initial API and implementation
 *    
 *******************************************************************************/
package org.jacoco.core.runtime;

import org.jacoco.core.data.AbstractExecutionDataStore;
import org.jacoco.core.data.ControlFlowExecutionData;
import org.jacoco.core.data.IExecutionDataVisitor;
import org.jacoco.core.data.ISessionInfoVisitor;
import org.jacoco.core.data.SessionInfo;
import org.jacoco.core.internal.instr.InstrSupport;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

/**
 * Container for runtime execution and meta data. All access to the runtime data
 * is thread safe.
 */
public abstract class AbstractRuntimeData {

    /** store for execution data */
    protected AbstractExecutionDataStore store;

    private long startTimeStamp;

    private String sessionId;

    /**
     * Creates a new runtime.
     */
    public AbstractRuntimeData() {
        sessionId = "<none>";
        startTimeStamp = System.currentTimeMillis();
    }

    /**
     * Sets a session identifier for this runtime. The identifier is used when
     * execution data is collected. If no identifier is explicitly set a
     * identifier is generated from the host name and a random number. This
     * method can be called at any time.
     * 
     * @see #collect(IExecutionDataVisitor, ISessionInfoVisitor, boolean)
     * @param id
     *            new session identifier
     */
    public void setSessionId(final String id) {
        sessionId = id;
    }

    /**
     * Get the current a session identifier for this runtime.
     * 
     * @see #setSessionId(String)
     * @return current session identifier
     */
    public String getSessionId() {
        return sessionId;
    }

    /**
     * Collects the current execution data and writes it to the given
     * {@link IExecutionDataVisitor} object.
     * 
     * @param executionDataVisitor
     *            handler to write coverage data to
     * @param sessionInfoVisitor
     *            handler to write session information to
     * @param reset
     *            if <code>true</code> the current coverage information is also
     *            cleared
     */
    public final void collect(final IExecutionDataVisitor executionDataVisitor,
            final ISessionInfoVisitor sessionInfoVisitor, final boolean reset) {
        synchronized (store) {
            final SessionInfo info = new SessionInfo(sessionId, startTimeStamp, System.currentTimeMillis());
            sessionInfoVisitor.visitSessionInfo(info);
            store.accept(executionDataVisitor);
            if (reset) {
                reset();
            }
        }
    }

    /**
     * Resets all coverage information.
     */
    public final void reset() {
        synchronized (store) {
            store.reset();
            startTimeStamp = System.currentTimeMillis();
        }
    }

    /**
     * Returns the coverage data for the class with the given identifier. If
     * there is no data available under the given id a new entry is created.
     * This is a synchronized access to the underlying store.
     * 
     * @param id
     *            class identifier
     * @param name
     *            VM name of the class
     * @param probecount
     *            probe data length
     * @return execution data
     */
    public abstract ControlFlowExecutionData getExecutionData(final Long id, final String name,
            final int probecount);

    /**
     * Retrieves the execution probe array for a given class. The passed
     * {@link Object} array instance is used for parameters and the return value
     * as follows. Call parameters:
     * 
     * <ul>
     * <li>args[0]: class id ({@link Long})
     * <li>args[1]: vm class name ({@link String})
     * <li>args[2]: probe count ({@link Integer})
     * </ul>
     * 
     * Return value:
     * 
     * <ul>
     * <li>args[0]: probe array (<code>boolean[]</code>)
     * </ul>
     * 
     * @param args
     *            parameter array of length 3
     */
    public void getProbes(final Object[] args) {
        final Long classid = (Long) args[0];
        final String name = (String) args[1];
        final int probecount = ((Integer) args[2]).intValue();
        args[0] = getExecutionData(classid, name, probecount).getProbes();
    }

    /**
     * In violation of the regular semantic of {@link Object#equals(Object)}
     * this implementation is used as the interface to the execution data store.
     * 
     * @param args
     *            the arguments as an {@link Object} array
     * @return has no meaning
     */
    @Override
    public boolean equals(final Object args) {
        if (args instanceof Object[]) {
            getProbes((Object[]) args);
        }
        return super.equals(args);
    }

    /**
     * Generates code that creates the argument array for the
     * {@link #getProbes(Object[])} method. The array instance is left on the
     * operand stack. The generated code requires a stack size of 5.
     * 
     * @param classid
     *            class identifier
     * @param classname
     *            VM class name
     * @param probecount
     *            probe count for this class
     * @param mv
     *            visitor to emit generated code
     */
    public static void generateArgumentArray(final long classid, final String classname, final int probecount,
            final MethodVisitor mv) {
        mv.visitInsn(Opcodes.ICONST_3);
        mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");

        // Class Id:
        mv.visitInsn(Opcodes.DUP);
        mv.visitInsn(Opcodes.ICONST_0);
        mv.visitLdcInsn(Long.valueOf(classid));
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
        mv.visitInsn(Opcodes.AASTORE);

        // Class Name:
        mv.visitInsn(Opcodes.DUP);
        mv.visitInsn(Opcodes.ICONST_1);
        mv.visitLdcInsn(classname);
        mv.visitInsn(Opcodes.AASTORE);

        // Probe Count:
        mv.visitInsn(Opcodes.DUP);
        mv.visitInsn(Opcodes.ICONST_2);
        InstrSupport.push(mv, probecount);
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
        mv.visitInsn(Opcodes.AASTORE);
    }

    /**
     * Generates the code that calls a {@link ControlFlowRuntimeData} instance through the
     * JRE API method {@link Object#equals(Object)}. The code pops a
     * {@link Object} instance from the stack and pushes the probe array of type
     * <code>boolean[]</code> on the operand stack. The generated code requires
     * a stack size of 6.
     * 
     * @param classid
     *            class identifier
     * @param classname
     *            VM class name
     * @param probecount
     *            probe count for this class
     * @param mv
     *            visitor to emit generated code
     */
    public static void generateAccessCall(final long classid, final String classname, final int probecount,
            final MethodVisitor mv) {
        // stack[0]: Ljava/lang/Object;

        generateArgumentArray(classid, classname, probecount, mv);

        // stack[1]: [Ljava/lang/Object;
        // stack[0]: Ljava/lang/Object;

        mv.visitInsn(Opcodes.DUP_X1);

        // stack[2]: [Ljava/lang/Object;
        // stack[1]: Ljava/lang/Object;
        // stack[0]: [Ljava/lang/Object;

        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false);
        mv.visitInsn(Opcodes.POP);

        // stack[0]: [Ljava/lang/Object;

        mv.visitInsn(Opcodes.ICONST_0);
        mv.visitInsn(Opcodes.AALOAD);

        // stack[0]: [Z

        mv.visitTypeInsn(Opcodes.CHECKCAST, InstrSupport.DATAFIELD_DESC);
    }

}