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