• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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