Home | History | Annotate | Download | only in runtime
      1 /*******************************************************************************
      2  * Copyright (c) 2009, 2018 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  *******************************************************************************/
     12 package org.jacoco.core.runtime;
     13 
     14 import org.jacoco.core.data.ExecutionData;
     15 import org.jacoco.core.data.ExecutionDataStore;
     16 import org.jacoco.core.data.IExecutionDataVisitor;
     17 import org.jacoco.core.data.ISessionInfoVisitor;
     18 import org.jacoco.core.data.SessionInfo;
     19 import org.jacoco.core.internal.instr.InstrSupport;
     20 import org.objectweb.asm.MethodVisitor;
     21 import org.objectweb.asm.Opcodes;
     22 
     23 /**
     24  * Container for runtime execution and meta data. All access to the runtime data
     25  * is thread safe.
     26  */
     27 public class RuntimeData {
     28 
     29 	/** store for execution data */
     30 	protected final ExecutionDataStore store;
     31 
     32 	private long startTimeStamp;
     33 
     34 	private String sessionId;
     35 
     36 	/**
     37 	 * Creates a new runtime.
     38 	 */
     39 	public RuntimeData() {
     40 		// BEGIN android-change
     41 		this(new ExecutionDataStore());
     42 		// END android-change
     43 	}
     44 
     45 	// BEGIN android-change
     46 	/**
     47 	 * Creates a new runtime, reusing an existing {@link ExecutionDataStore}.
     48 	 *
     49 	 * @param store
     50 	 *            the store to reuse
     51 	 */
     52 	public RuntimeData(ExecutionDataStore store) {
     53 		this.store = store;
     54 		sessionId = "<none>";
     55 		startTimeStamp = System.currentTimeMillis();
     56 	}
     57 	// END android-change
     58 
     59 	/**
     60 	 * Sets a session identifier for this runtime. The identifier is used when
     61 	 * execution data is collected. If no identifier is explicitly set a
     62 	 * identifier is generated from the host name and a random number. This
     63 	 * method can be called at any time.
     64 	 *
     65 	 * @see #collect(IExecutionDataVisitor, ISessionInfoVisitor, boolean)
     66 	 * @param id
     67 	 *            new session identifier
     68 	 */
     69 	public void setSessionId(final String id) {
     70 		sessionId = id;
     71 	}
     72 
     73 	/**
     74 	 * Get the current a session identifier for this runtime.
     75 	 *
     76 	 * @see #setSessionId(String)
     77 	 * @return current session identifier
     78 	 */
     79 	public String getSessionId() {
     80 		return sessionId;
     81 	}
     82 
     83 	/**
     84 	 * Collects the current execution data and writes it to the given
     85 	 * {@link IExecutionDataVisitor} object.
     86 	 *
     87 	 * @param executionDataVisitor
     88 	 *            handler to write coverage data to
     89 	 * @param sessionInfoVisitor
     90 	 *            handler to write session information to
     91 	 * @param reset
     92 	 *            if <code>true</code> the current coverage information is also
     93 	 *            cleared
     94 	 */
     95 	public final void collect(final IExecutionDataVisitor executionDataVisitor,
     96 			final ISessionInfoVisitor sessionInfoVisitor, final boolean reset) {
     97 		synchronized (store) {
     98 			final SessionInfo info = new SessionInfo(sessionId, startTimeStamp,
     99 					System.currentTimeMillis());
    100 			sessionInfoVisitor.visitSessionInfo(info);
    101 			store.accept(executionDataVisitor);
    102 			if (reset) {
    103 				reset();
    104 			}
    105 		}
    106 	}
    107 
    108 	/**
    109 	 * Resets all coverage information.
    110 	 */
    111 	public final void reset() {
    112 		synchronized (store) {
    113 			store.reset();
    114 			startTimeStamp = System.currentTimeMillis();
    115 		}
    116 	}
    117 
    118 	/**
    119 	 * Returns the coverage data for the class with the given identifier. If
    120 	 * there is no data available under the given id a new entry is created.
    121 	 * This is a synchronized access to the underlying store.
    122 	 *
    123 	 * @param id
    124 	 *            class identifier
    125 	 * @param name
    126 	 *            VM name of the class
    127 	 * @param probecount
    128 	 *            probe data length
    129 	 * @return execution data
    130 	 */
    131 	public ExecutionData getExecutionData(final Long id, final String name,
    132 			final int probecount) {
    133 		synchronized (store) {
    134 			return store.get(id, name, probecount);
    135 		}
    136 	}
    137 
    138 	/**
    139 	 * Retrieves the execution probe array for a given class. The passed
    140 	 * {@link Object} array instance is used for parameters and the return value
    141 	 * as follows. Call parameters:
    142 	 *
    143 	 * <ul>
    144 	 * <li>args[0]: class id ({@link Long})
    145 	 * <li>args[1]: vm class name ({@link String})
    146 	 * <li>args[2]: probe count ({@link Integer})
    147 	 * </ul>
    148 	 *
    149 	 * Return value:
    150 	 *
    151 	 * <ul>
    152 	 * <li>args[0]: probe array (<code>boolean[]</code>)
    153 	 * </ul>
    154 	 *
    155 	 * @param args
    156 	 *            parameter array of length 3
    157 	 */
    158 	public void getProbes(final Object[] args) {
    159 		final Long classid = (Long) args[0];
    160 		final String name = (String) args[1];
    161 		final int probecount = ((Integer) args[2]).intValue();
    162 		args[0] = getExecutionData(classid, name, probecount).getProbes();
    163 	}
    164 
    165 	/**
    166 	 * In violation of the regular semantic of {@link Object#equals(Object)}
    167 	 * this implementation is used as the interface to the execution data store.
    168 	 *
    169 	 * @param args
    170 	 *            the arguments as an {@link Object} array
    171 	 * @return has no meaning
    172 	 */
    173 	@Override
    174 	public boolean equals(final Object args) {
    175 		if (args instanceof Object[]) {
    176 			getProbes((Object[]) args);
    177 		}
    178 		return super.equals(args);
    179 	}
    180 
    181 	/**
    182 	 * Generates code that creates the argument array for the
    183 	 * {@link #getProbes(Object[])} method. The array instance is left on the
    184 	 * operand stack. The generated code requires a stack size of 5.
    185 	 *
    186 	 * @param classid
    187 	 *            class identifier
    188 	 * @param classname
    189 	 *            VM class name
    190 	 * @param probecount
    191 	 *            probe count for this class
    192 	 * @param mv
    193 	 *            visitor to emit generated code
    194 	 */
    195 	public static void generateArgumentArray(final long classid,
    196 			final String classname, final int probecount, final MethodVisitor mv) {
    197 		mv.visitInsn(Opcodes.ICONST_3);
    198 		mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
    199 
    200 		// Class Id:
    201 		mv.visitInsn(Opcodes.DUP);
    202 		mv.visitInsn(Opcodes.ICONST_0);
    203 		mv.visitLdcInsn(Long.valueOf(classid));
    204 		mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf",
    205 				"(J)Ljava/lang/Long;", false);
    206 		mv.visitInsn(Opcodes.AASTORE);
    207 
    208 		// Class Name:
    209 		mv.visitInsn(Opcodes.DUP);
    210 		mv.visitInsn(Opcodes.ICONST_1);
    211 		mv.visitLdcInsn(classname);
    212 		mv.visitInsn(Opcodes.AASTORE);
    213 
    214 		// Probe Count:
    215 		mv.visitInsn(Opcodes.DUP);
    216 		mv.visitInsn(Opcodes.ICONST_2);
    217 		InstrSupport.push(mv, probecount);
    218 		mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer",
    219 				"valueOf", "(I)Ljava/lang/Integer;", false);
    220 		mv.visitInsn(Opcodes.AASTORE);
    221 	}
    222 
    223 	/**
    224 	 * Generates the code that calls a {@link RuntimeData} instance through the
    225 	 * JRE API method {@link Object#equals(Object)}. The code pops a
    226 	 * {@link Object} instance from the stack and pushes the probe array of type
    227 	 * <code>boolean[]</code> on the operand stack. The generated code requires
    228 	 * a stack size of 6.
    229 	 *
    230 	 * @param classid
    231 	 *            class identifier
    232 	 * @param classname
    233 	 *            VM class name
    234 	 * @param probecount
    235 	 *            probe count for this class
    236 	 * @param mv
    237 	 *            visitor to emit generated code
    238 	 */
    239 	public static void generateAccessCall(final long classid,
    240 			final String classname, final int probecount, final MethodVisitor mv) {
    241 		// stack[0]: Ljava/lang/Object;
    242 
    243 		generateArgumentArray(classid, classname, probecount, mv);
    244 
    245 		// stack[1]: [Ljava/lang/Object;
    246 		// stack[0]: Ljava/lang/Object;
    247 
    248 		mv.visitInsn(Opcodes.DUP_X1);
    249 
    250 		// stack[2]: [Ljava/lang/Object;
    251 		// stack[1]: Ljava/lang/Object;
    252 		// stack[0]: [Ljava/lang/Object;
    253 
    254 		mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "equals",
    255 				"(Ljava/lang/Object;)Z", false);
    256 		mv.visitInsn(Opcodes.POP);
    257 
    258 		// stack[0]: [Ljava/lang/Object;
    259 
    260 		mv.visitInsn(Opcodes.ICONST_0);
    261 		mv.visitInsn(Opcodes.AALOAD);
    262 
    263 		// stack[0]: [Z
    264 
    265 		mv.visitTypeInsn(Opcodes.CHECKCAST, InstrSupport.DATAFIELD_DESC);
    266 	}
    267 
    268 }
    269