| /******************************************************************************* |
| * Copyright (c) 2009, 2019 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.IExecutionData; |
| import org.jacoco.core.data.ExecutionDataStore; |
| 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 class RuntimeData { |
| |
| /** store for execution data */ |
| protected final ExecutionDataStore store; |
| |
| private long startTimeStamp; |
| |
| private String sessionId; |
| |
| /** |
| * Creates a new runtime. |
| */ |
| public RuntimeData() { |
| // BEGIN android-change |
| this(new ExecutionDataStore()); |
| // END android-change |
| } |
| |
| // BEGIN android-change |
| /** |
| * Creates a new runtime, reusing an existing {@link ExecutionDataStore}. |
| * |
| * @param store |
| * the store to reuse |
| */ |
| public RuntimeData(ExecutionDataStore store) { |
| this.store = store; |
| sessionId = "<none>"; |
| startTimeStamp = System.currentTimeMillis(); |
| } |
| // END android-change |
| |
| /** |
| * 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 |
| */ |
| // BEGIN android-change |
| public IExecutionData getExecutionData(final Long id, final String name, |
| final int probecount) { |
| // END android-change |
| synchronized (store) { |
| return store.get(id, name, probecount); |
| } |
| } |
| |
| // BEGIN android-change |
| /** |
| * Retrieves the execution data 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]: execution data ({@link IExecutionData}) |
| * </ul> |
| * |
| * @param args |
| * parameter array of length 3 |
| */ |
| public void getExecutionData(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); |
| } |
| // END android-change |
| |
| /** |
| * 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[]) { |
| // BEGIN android-change |
| getExecutionData((Object[]) args); |
| // END android-change |
| } |
| 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 RuntimeData} 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); |
| } |
| |
| } |