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