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