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 Set<Label> targetLabels;
37
38 private int blockCount;
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 access
46 * the method's access flags
47 * @param name
48 * the method's name.
49 * @param desc
50 * the method's descriptor
51 * @param signature
52 * the method's signature. May be <tt>null</tt>.
53 * @param exceptions
54 * the internal names of the method's exception classes. May be
55 * <tt>null</tt>.
56 */
57 public BlockMethodAdapter(IBlockMethodVisitor blockVisitor,
58 final int access, final String name, final String desc,
59 final String signature, final String[] exceptions) {
60 super(access, name, desc, signature, exceptions);
61 this.blockVisitor = blockVisitor;
62 this.targetLabels = new HashSet<Label>();
63 this.blockCount = 0;
64 }
65
66 /**
67 * Returns the number of blocks found in the method. A valid return value
68 * can only be expected after {@link #visitEnd()} has been called.
69 *
70 * @return number of block in the method
71 */
72 public int getBlockCount() {
73 return blockCount;
74 }
75
76 // === MethodVisitor ===
77
78 @Override
79 public void visitJumpInsn(int opcode, Label label) {
80 targetLabels.add(label);
81 super.visitJumpInsn(opcode, label);
82 }
83
84 @Override
85 public void visitTableSwitchInsn(int min, int max, Label dflt,
86 Label[] labels) {
87 targetLabels.add(dflt);
88 for (final Label l : labels) {
89 targetLabels.add(l);
90 }
91 super.visitTableSwitchInsn(min, max, dflt, labels);
92 }
93
94 @Override
95 public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
96 targetLabels.add(dflt);
97 for (final Label l : labels) {
98 targetLabels.add(l);
99 }
100 super.visitLookupSwitchInsn(dflt, keys, labels);
101 }
102
103 @Override
104 public void visitTryCatchBlock(Label start, Label end, Label handler,
105 String type) {
106 targetLabels.add(start);
107 targetLabels.add(end);
108 targetLabels.add(handler);
109 super.visitTryCatchBlock(start, end, handler, type);
110 }
111
112 @Override
113 public void visitEnd() {
114 accept(new BlockFinder());
115 }
116
117 private final class BlockFinder extends MethodAdapter {
118
119 private boolean blockStarted;
120
121 public BlockFinder() {
122 super(blockVisitor);
123 blockStarted = false;
124 }
125
126 private void onBlockEndBeforeJump() {
127 if (blockStarted) {
128 blockVisitor.visitBlockEndBeforeJump(blockCount);
129 }
130 }
131
132 private void onBlockEnd() {
133 if (blockStarted) {
134 blockVisitor.visitBlockEnd(blockCount);
135 blockCount++;
136 blockStarted = false;
137 }
138 }
139
140 @Override
141 public void visitLabel(Label label) {
142 if (targetLabels.contains(label)) {
143 onBlockEndBeforeJump();
144 onBlockEnd();
145 }
146 super.visitLabel(label);
147 }
148
149 @Override
150 public void visitJumpInsn(int opcode, Label label) {
151 blockStarted = true;
152 onBlockEndBeforeJump();
153 super.visitJumpInsn(opcode, label);
154 onBlockEnd();
155 }
156
157 @Override
158 public void visitInsn(int opcode) {
159 blockStarted = true;
160 switch (opcode) {
161 case Opcodes.RETURN:
162 case Opcodes.IRETURN:
163 case Opcodes.FRETURN:
164 case Opcodes.LRETURN:
165 case Opcodes.DRETURN:
166 case Opcodes.ARETURN:
167 case Opcodes.ATHROW:
168 onBlockEndBeforeJump();
169 super.visitInsn(opcode);
170 onBlockEnd();
171 break;
172 default:
173 super.visitInsn(opcode);
174 break;
175 }
176 }
177
178 @Override
179 public void visitTableSwitchInsn(int min, int max, Label dflt,
180 Label[] labels) {
181 blockStarted = true;
182 onBlockEndBeforeJump();
183 super.visitTableSwitchInsn(min, max, dflt, labels);
184 onBlockEnd();
185 }
186
187 @Override
188 public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
189 blockStarted = true;
190 onBlockEndBeforeJump();
191 super.visitLookupSwitchInsn(dflt, keys, labels);
192 onBlockEnd();
193 }
194
195 @Override
196 public void visitFieldInsn(int opcode, String owner, String name,
197 String desc) {
198 blockStarted = true;
199 super.visitFieldInsn(opcode, owner, name, desc);
200 }
201
202 @Override
203 public void visitIincInsn(int var, int increment) {
204 blockStarted = true;
205 super.visitIincInsn(var, increment);
206 }
207
208 @Override
209 public void visitIntInsn(int opcode, int operand) {
210 blockStarted = true;
211 super.visitIntInsn(opcode, operand);
212 }
213
214 @Override
215 public void visitLdcInsn(Object cst) {
216 blockStarted = true;
217 super.visitLdcInsn(cst);
218 }
219
220 @Override
221 public void visitMethodInsn(int opcode, String owner, String name,
222 String desc) {
223 blockStarted = true;
224 super.visitMethodInsn(opcode, owner, name, desc);
225 }
226
227 @Override
228 public void visitMultiANewArrayInsn(String desc, int dims) {
229 blockStarted = true;
230 super.visitMultiANewArrayInsn(desc, dims);
231 }
232
233 @Override
234 public void visitTypeInsn(int opcode, String type) {
235 blockStarted = true;
236 super.visitTypeInsn(opcode, type);
237 }
238
239 @Override
240 public void visitVarInsn(int opcode, int var) {
241 blockStarted = true;
242 super.visitVarInsn(opcode, var);
243 }
244
245 }
246
247}