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 *******************************************************************************/
12package org.jacoco.core.internal.flow;
13
14import org.jacoco.core.JaCoCo;
15import org.objectweb.asm.Handle;
16import org.objectweb.asm.Label;
17import org.objectweb.asm.MethodVisitor;
18import org.objectweb.asm.Opcodes;
19import org.objectweb.asm.tree.MethodNode;
20
21/**
22 * Method visitor to collect flow related information about the {@link Label}s
23 * within a class. It calculates the properties "multitarget" and "successor"
24 * that can afterwards be obtained via {@link LabelInfo}.
25 */
26public final class LabelFlowAnalyzer extends MethodVisitor {
27
28	/**
29	 * Marks all labels of the method with control flow information.
30	 *
31	 * @param method
32	 *            Method to mark labels
33	 */
34	public static void markLabels(final MethodNode method) {
35		// We do not use the accept() method as ASM resets labels after every
36		// call to accept()
37		final MethodVisitor lfa = new LabelFlowAnalyzer();
38		for (int i = method.tryCatchBlocks.size(); --i >= 0;) {
39			method.tryCatchBlocks.get(i).accept(lfa);
40		}
41		method.instructions.accept(lfa);
42	}
43
44	/**
45	 * <code>true</code> if the current instruction is a potential successor of
46	 * the previous instruction. Accessible for testing.
47	 */
48	boolean successor = false;
49
50	/**
51	 * <code>true</code> for the very first instruction only. Accessible for
52	 * testing.
53	 */
54	boolean first = true;
55
56	/**
57	 * Label instance of the last line start.
58	 */
59	Label lineStart = null;
60
61	/**
62	 * Create new instance.
63	 */
64	public LabelFlowAnalyzer() {
65		super(JaCoCo.ASM_API_VERSION);
66	}
67
68	@Override
69	public void visitTryCatchBlock(final Label start, final Label end,
70			final Label handler, final String type) {
71		// Enforce probe at the beginning of the block. Assuming the start of
72		// the block already is successor of some other code, adding a target
73		// makes the start a multitarget. However, if the start of the block
74		// also is the start of the method, no probe will be added.
75		LabelInfo.setTarget(start);
76
77		// Mark exception handler as possible target of the block
78		LabelInfo.setTarget(handler);
79	}
80
81	@Override
82	public void visitJumpInsn(final int opcode, final Label label) {
83		LabelInfo.setTarget(label);
84		if (opcode == Opcodes.JSR) {
85			throw new AssertionError("Subroutines not supported.");
86		}
87		successor = opcode != Opcodes.GOTO;
88		first = false;
89	}
90
91	@Override
92	public void visitLabel(final Label label) {
93		if (first) {
94			LabelInfo.setTarget(label);
95		}
96		if (successor) {
97			LabelInfo.setSuccessor(label);
98		}
99	}
100
101	@Override
102	public void visitLineNumber(final int line, final Label start) {
103		lineStart = start;
104	}
105
106	@Override
107	public void visitTableSwitchInsn(final int min, final int max,
108			final Label dflt, final Label... labels) {
109		visitSwitchInsn(dflt, labels);
110	}
111
112	@Override
113	public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
114			final Label[] labels) {
115		visitSwitchInsn(dflt, labels);
116	}
117
118	private void visitSwitchInsn(final Label dflt, final Label[] labels) {
119		LabelInfo.resetDone(dflt);
120		LabelInfo.resetDone(labels);
121		setTargetIfNotDone(dflt);
122		for (final Label l : labels) {
123			setTargetIfNotDone(l);
124		}
125		successor = false;
126		first = false;
127	}
128
129	private static void setTargetIfNotDone(final Label label) {
130		if (!LabelInfo.isDone(label)) {
131			LabelInfo.setTarget(label);
132			LabelInfo.setDone(label);
133		}
134	}
135
136	@Override
137	public void visitInsn(final int opcode) {
138		switch (opcode) {
139		case Opcodes.RET:
140			throw new AssertionError("Subroutines not supported.");
141		case Opcodes.IRETURN:
142		case Opcodes.LRETURN:
143		case Opcodes.FRETURN:
144		case Opcodes.DRETURN:
145		case Opcodes.ARETURN:
146		case Opcodes.RETURN:
147		case Opcodes.ATHROW:
148			successor = false;
149			break;
150		default:
151			successor = true;
152			break;
153		}
154		first = false;
155	}
156
157	@Override
158	public void visitIntInsn(final int opcode, final int operand) {
159		successor = true;
160		first = false;
161	}
162
163	@Override
164	public void visitVarInsn(final int opcode, final int var) {
165		successor = true;
166		first = false;
167	}
168
169	@Override
170	public void visitTypeInsn(final int opcode, final String type) {
171		successor = true;
172		first = false;
173	}
174
175	@Override
176	public void visitFieldInsn(final int opcode, final String owner,
177			final String name, final String desc) {
178		successor = true;
179		first = false;
180	}
181
182	@Override
183	public void visitMethodInsn(final int opcode, final String owner,
184			final String name, final String desc, final boolean itf) {
185		successor = true;
186		first = false;
187		markMethodInvocationLine();
188	}
189
190	@Override
191	public void visitInvokeDynamicInsn(final String name, final String desc,
192			final Handle bsm, final Object... bsmArgs) {
193		successor = true;
194		first = false;
195		markMethodInvocationLine();
196	}
197
198	private void markMethodInvocationLine() {
199		if (lineStart != null) {
200			LabelInfo.setMethodInvocationLine(lineStart);
201		}
202	}
203
204	@Override
205	public void visitLdcInsn(final Object cst) {
206		successor = true;
207		first = false;
208	}
209
210	@Override
211	public void visitIincInsn(final int var, final int increment) {
212		successor = true;
213		first = false;
214	}
215
216	@Override
217	public void visitMultiANewArrayInsn(final String desc, final int dims) {
218		successor = true;
219		first = false;
220	}
221
222}
223