BlockMethodAdapter.java
1/*******************************************************************************
2 * Copyright (c) 2009, 2010 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.instr;
13
14import org.objectweb.asm.Label;
15import org.objectweb.asm.MethodAdapter;
16import org.objectweb.asm.Opcodes;
17import org.objectweb.asm.tree.LabelNode;
18import org.objectweb.asm.tree.MethodNode;
19
20/**
21 * A method visitor that determines block boundaries and reports them to the
22 * wrapped visitor. The implementation first buffers the content of the method
23 * to extract all control flow target labels. At the end of the method it
24 * flushes the content to the next visitor.
25 *
26 * @author Marc R. Hoffmann
27 * @version 0.4.1.20101007204400
28 */
29final class BlockMethodAdapter extends MethodNode {
30
31 private final IBlockMethodVisitor blockVisitor;
32
33 private final IProbeIdGenerator idGenerator;
34
35 /**
36 * Create a new adapter for the given block visitor.
37 *
38 * @param blockVisitor
39 * visitor to report block boundaries to
40 * @param idGenerator
41 * generator for probe ids
42 * @param access
43 * the method's access flags
44 * @param name
45 * the method's name.
46 * @param desc
47 * the method's descriptor
48 * @param signature
49 * the method's signature. May be <tt>null</tt>.
50 * @param exceptions
51 * the internal names of the method's exception classes. May be
52 * <tt>null</tt>.
53 */
54 public BlockMethodAdapter(final IBlockMethodVisitor blockVisitor,
55 final IProbeIdGenerator idGenerator, final int access,
56 final String name, final String desc, final String signature,
57 final String[] exceptions) {
58 super(access, name, desc, signature, exceptions);
59 this.blockVisitor = blockVisitor;
60 this.idGenerator = idGenerator;
61 }
62
63 // === MethodVisitor ===
64
65 @Override
66 public void visitJumpInsn(final int opcode, final Label label) {
67 super.visitJumpInsn(opcode, label);
68 setTarget(label);
69 }
70
71 @Override
72 public void visitTableSwitchInsn(final int min, final int max,
73 final Label dflt, final Label[] labels) {
74 super.visitTableSwitchInsn(min, max, dflt, labels);
75 setTarget(dflt);
76 for (final Label l : labels) {
77 setTarget(l);
78 }
79 }
80
81 @Override
82 public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
83 final Label[] labels) {
84 super.visitLookupSwitchInsn(dflt, keys, labels);
85 setTarget(dflt);
86 for (final Label l : labels) {
87 setTarget(l);
88 }
89 }
90
91 @Override
92 public void visitTryCatchBlock(final Label start, final Label end,
93 final Label handler, final String type) {
94 super.visitTryCatchBlock(start, end, handler, type);
95 setTarget(start);
96 setTarget(end);
97 setTarget(handler);
98 }
99
100 @Override
101 public void visitEnd() {
102 accept(new BlockFinder());
103 }
104
105 private final class BlockFinder extends MethodAdapter {
106
107 private boolean blockStarted;
108
109 private int id;
110
111 public BlockFinder() {
112 super(blockVisitor);
113 blockStarted = false;
114 }
115
116 private void onBlockEndBeforeJump() {
117 if (blockStarted) {
118 id = idGenerator.nextId();
119 blockVisitor.visitBlockEndBeforeJump(id);
120 }
121 }
122
123 private void onBlockEnd() {
124 if (blockStarted) {
125 blockVisitor.visitBlockEnd(id);
126 blockStarted = false;
127 }
128 }
129
130 @Override
131 public void visitLabel(final Label label) {
132 if (isTarget(label)) {
133 onBlockEndBeforeJump();
134 onBlockEnd();
135 }
136 super.visitLabel(label);
137 }
138
139 @Override
140 public void visitJumpInsn(final int opcode, final Label label) {
141 blockStarted = true;
142 onBlockEndBeforeJump();
143 super.visitJumpInsn(opcode, label);
144 onBlockEnd();
145 }
146
147 @Override
148 public void visitInsn(final int opcode) {
149 blockStarted = true;
150 switch (opcode) {
151 case Opcodes.RETURN:
152 case Opcodes.IRETURN:
153 case Opcodes.FRETURN:
154 case Opcodes.LRETURN:
155 case Opcodes.DRETURN:
156 case Opcodes.ARETURN:
157 case Opcodes.ATHROW:
158 onBlockEndBeforeJump();
159 super.visitInsn(opcode);
160 onBlockEnd();
161 break;
162 default:
163 super.visitInsn(opcode);
164 break;
165 }
166 }
167
168 @Override
169 public void visitTableSwitchInsn(final int min, final int max,
170 final Label dflt, final Label[] labels) {
171 blockStarted = true;
172 onBlockEndBeforeJump();
173 super.visitTableSwitchInsn(min, max, dflt, labels);
174 onBlockEnd();
175 }
176
177 @Override
178 public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
179 final Label[] labels) {
180 blockStarted = true;
181 onBlockEndBeforeJump();
182 super.visitLookupSwitchInsn(dflt, keys, labels);
183 onBlockEnd();
184 }
185
186 @Override
187 public void visitFieldInsn(final int opcode, final String owner,
188 final String name, final String desc) {
189 blockStarted = true;
190 super.visitFieldInsn(opcode, owner, name, desc);
191 }
192
193 @Override
194 public void visitIincInsn(final int var, final int increment) {
195 blockStarted = true;
196 super.visitIincInsn(var, increment);
197 }
198
199 @Override
200 public void visitIntInsn(final int opcode, final int operand) {
201 blockStarted = true;
202 super.visitIntInsn(opcode, operand);
203 }
204
205 @Override
206 public void visitLdcInsn(final Object cst) {
207 blockStarted = true;
208 super.visitLdcInsn(cst);
209 }
210
211 @Override
212 public void visitMethodInsn(final int opcode, final String owner,
213 final String name, final String desc) {
214 blockStarted = true;
215 super.visitMethodInsn(opcode, owner, name, desc);
216 }
217
218 @Override
219 public void visitMultiANewArrayInsn(final String desc, final int dims) {
220 blockStarted = true;
221 super.visitMultiANewArrayInsn(desc, dims);
222 }
223
224 @Override
225 public void visitTypeInsn(final int opcode, final String type) {
226 blockStarted = true;
227 super.visitTypeInsn(opcode, type);
228 }
229
230 @Override
231 public void visitVarInsn(final int opcode, final int var) {
232 blockStarted = true;
233 super.visitVarInsn(opcode, var);
234 }
235
236 }
237
238 // === Utilities for marking jump target labels: ===
239
240 private static final class TargetMarker {
241
242 final LabelNode labelNode;
243
244 TargetMarker(final LabelNode labelNode) {
245 this.labelNode = labelNode;
246 }
247
248 }
249
250 @Override
251 protected LabelNode getLabelNode(final Label l) {
252 if (l.info instanceof TargetMarker) {
253 return ((TargetMarker) l.info).labelNode;
254 }
255 return super.getLabelNode(l);
256 }
257
258 private final void setTarget(final Label l) {
259 if (!isTarget(l)) {
260 l.info = new TargetMarker(getLabelNode(l));
261 }
262 }
263
264 private final boolean isTarget(final Label l) {
265 return l.info instanceof TargetMarker;
266 }
267
268}