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