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.internal.flow;
13 
14 import org.objectweb.asm.Label;
15 
16 /**
17  * Data container that is attached to {@link Label#info} objects to store flow
18  * and instrumentation specific information. The information is only valid
19  * locally in specific contexts.
20  */
21 public final class LabelInfo {
22 
23 	/**
24 	 * Reserved ID for "no probe".
25 	 */
26 	public static final int NO_PROBE = -1;
27 
28 	private boolean target = false;
29 
30 	private boolean multiTarget = false;
31 
32 	private boolean successor = false;
33 
34 	private boolean methodInvocationLine = false;
35 
36 	private boolean done = false;
37 
38 	private int probeid = NO_PROBE;
39 
40 	private Label intermediate = null;
41 
42 	private Instruction instruction = null;
43 
44 	// instances are only created within this class
LabelInfo()45 	private LabelInfo() {
46 	}
47 
48 	/**
49 	 * Defines that the given label is a jump target.
50 	 *
51 	 * @param label
52 	 *            label to define
53 	 */
setTarget(final Label label)54 	public static void setTarget(final Label label) {
55 		final LabelInfo info = create(label);
56 		if (info.target || info.successor) {
57 			info.multiTarget = true;
58 		} else {
59 			info.target = true;
60 		}
61 	}
62 
63 	/**
64 	 * Defines that the given label is the possible successor of the previous
65 	 * instruction in the method.
66 	 *
67 	 * @param label
68 	 *            label to define
69 	 */
setSuccessor(final Label label)70 	public static void setSuccessor(final Label label) {
71 		final LabelInfo info = create(label);
72 		info.successor = true;
73 		if (info.target) {
74 			info.multiTarget = true;
75 		}
76 	}
77 
78 	/**
79 	 * Checks whether multiple control paths lead to a label. Control flow path
80 	 * to a certain label are: jump targets, exception handlers and normal
81 	 * control flow from its predecessor instruction (unless this is an
82 	 * unconditional jump or method exit).
83 	 *
84 	 * @param label
85 	 *            label to check
86 	 * @return <code>true</code> if the given multiple control paths lead to the
87 	 *         given label
88 	 */
isMultiTarget(final Label label)89 	public static boolean isMultiTarget(final Label label) {
90 		final LabelInfo info = get(label);
91 		return info == null ? false : info.multiTarget;
92 	}
93 
94 	/**
95 	 * Checks whether this label is the possible successor of the previous
96 	 * instruction in the method. This is the case if the predecessor isn't a
97 	 * unconditional jump or method exit instruction.
98 	 *
99 	 * @param label
100 	 *            label to check
101 	 * @return <code>true</code> if the label is a possible instruction
102 	 *         successor
103 	 */
isSuccessor(final Label label)104 	public static boolean isSuccessor(final Label label) {
105 		final LabelInfo info = get(label);
106 		return info == null ? false : info.successor;
107 	}
108 
109 	/**
110 	 * Mark a given label as the beginning of a line with method invocations.
111 	 *
112 	 * @param label
113 	 *            label to mark
114 	 */
setMethodInvocationLine(final Label label)115 	public static void setMethodInvocationLine(final Label label) {
116 		create(label).methodInvocationLine = true;
117 	}
118 
119 	/**
120 	 * Checks whether the a given label has been marked as a line with method
121 	 * invocations.
122 	 *
123 	 * @param label
124 	 *            label to check
125 	 * @return <code>true</code> if the label represents a line with method
126 	 *         invocations
127 	 */
isMethodInvocationLine(final Label label)128 	public static boolean isMethodInvocationLine(final Label label) {
129 		final LabelInfo info = get(label);
130 		return info == null ? false : info.methodInvocationLine;
131 	}
132 
133 	/**
134 	 * Determines whether the given label needs a probe to be inserted before.
135 	 *
136 	 * @param label
137 	 *            label to test
138 	 * @return <code>true</code> if a probe should be inserted before
139 	 */
needsProbe(final Label label)140 	public static boolean needsProbe(final Label label) {
141 		final LabelInfo info = get(label);
142 		return info != null && info.successor
143 				&& (info.multiTarget || info.methodInvocationLine);
144 	}
145 
146 	/**
147 	 * Mark a given label as done.
148 	 *
149 	 * @param label
150 	 *            label to mark
151 	 */
setDone(final Label label)152 	public static void setDone(final Label label) {
153 		create(label).done = true;
154 	}
155 
156 	/**
157 	 * Resets the "done" status of a given label.
158 	 *
159 	 * @param label
160 	 *            label to reset
161 	 */
resetDone(final Label label)162 	public static void resetDone(final Label label) {
163 		final LabelInfo info = get(label);
164 		if (info != null) {
165 			info.done = false;
166 		}
167 	}
168 
169 	/**
170 	 * Resets the "done" status of all given labels.
171 	 *
172 	 * @param labels
173 	 *            labels to reset
174 	 */
resetDone(final Label[] labels)175 	public static void resetDone(final Label[] labels) {
176 		for (final Label label : labels) {
177 			resetDone(label);
178 		}
179 	}
180 
181 	/**
182 	 * Checks whether this label is marked as done.
183 	 *
184 	 * @param label
185 	 *            label to check
186 	 * @return <code>true</code> if this label is marked as done
187 	 */
isDone(final Label label)188 	public static boolean isDone(final Label label) {
189 		final LabelInfo info = get(label);
190 		return info == null ? false : info.done;
191 	}
192 
193 	/**
194 	 * Sets the given probe id to the given label.
195 	 *
196 	 * @param label
197 	 *            label to assign a probe to
198 	 * @param id
199 	 *            id of the probe
200 	 */
setProbeId(final Label label, final int id)201 	public static void setProbeId(final Label label, final int id) {
202 		create(label).probeid = id;
203 	}
204 
205 	/**
206 	 * Returns the assigned probe id.
207 	 *
208 	 * @param label
209 	 *            label to check
210 	 * @return probe id or {@link #NO_PROBE} if no probe is assigned to the
211 	 *         label
212 	 */
getProbeId(final Label label)213 	public static int getProbeId(final Label label) {
214 		final LabelInfo info = get(label);
215 		return info == null ? NO_PROBE : info.probeid;
216 	}
217 
218 	/**
219 	 * Defines an intermediate label for the given label. Such intermediate
220 	 * labels are required during instrumentation to add probes to jump targets.
221 	 *
222 	 * @param label
223 	 *            label to define for
224 	 * @param intermediate
225 	 *            intermediate label
226 	 */
setIntermediateLabel(final Label label, final Label intermediate)227 	public static void setIntermediateLabel(final Label label,
228 			final Label intermediate) {
229 		create(label).intermediate = intermediate;
230 	}
231 
232 	/**
233 	 * Returns the intermediate label for the given label if one has been
234 	 * defined.
235 	 *
236 	 * @param label
237 	 *            label to look for
238 	 * @return intermediate label or <code>null</code>
239 	 */
getIntermediateLabel(final Label label)240 	public static Label getIntermediateLabel(final Label label) {
241 		final LabelInfo info = get(label);
242 		return info == null ? null : info.intermediate;
243 	}
244 
245 	/**
246 	 * Sets the instruction corresponding to this label.
247 	 *
248 	 * @param label
249 	 *            label to set the instruction for
250 	 * @param instruction
251 	 *            corresponding instruction
252 	 */
setInstruction(final Label label, final Instruction instruction)253 	public static void setInstruction(final Label label,
254 			final Instruction instruction) {
255 		create(label).instruction = instruction;
256 	}
257 
258 	/**
259 	 * Returns the corresponding instruction for the given label if one has been
260 	 * defined.
261 	 *
262 	 * @param label
263 	 *            label to look for
264 	 * @return corresponding instruction or <code>null</code>
265 	 */
getInstruction(final Label label)266 	public static Instruction getInstruction(final Label label) {
267 		final LabelInfo info = get(label);
268 		return info == null ? null : info.instruction;
269 	}
270 
get(final Label label)271 	private static LabelInfo get(final Label label) {
272 		final Object info = label.info;
273 		return info instanceof LabelInfo ? (LabelInfo) info : null;
274 	}
275 
create(final Label label)276 	private static LabelInfo create(final Label label) {
277 		LabelInfo info = get(label);
278 		if (info == null) {
279 			info = new LabelInfo();
280 			label.info = info;
281 		}
282 		return info;
283 	}
284 
285 }
286