• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright (c) 2009, 2018 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 java.util.HashMap;
15 import java.util.Map;
16 
17 import org.jacoco.core.internal.instr.InstrSupport;
18 import org.objectweb.asm.Label;
19 import org.objectweb.asm.MethodVisitor;
20 import org.objectweb.asm.Opcodes;
21 import org.objectweb.asm.commons.AnalyzerAdapter;
22 
23 /**
24  * Adapter that creates additional visitor events for probes to be inserted into
25  * a method.
26  */
27 public final class MethodProbesAdapter extends MethodVisitor {
28 
29 	private final MethodProbesVisitor probesVisitor;
30 
31 	private final IProbeIdGenerator idGenerator;
32 
33 	private AnalyzerAdapter analyzer;
34 
35 	private final Map<Label, Label> tryCatchProbeLabels;
36 
37 	/**
38 	 * Create a new adapter instance.
39 	 *
40 	 * @param probesVisitor
41 	 *            visitor to delegate to
42 	 * @param idGenerator
43 	 *            generator for unique probe ids
44 	 */
MethodProbesAdapter(final MethodProbesVisitor probesVisitor, final IProbeIdGenerator idGenerator)45 	public MethodProbesAdapter(final MethodProbesVisitor probesVisitor,
46 			final IProbeIdGenerator idGenerator) {
47 		super(InstrSupport.ASM_API_VERSION, probesVisitor);
48 		this.probesVisitor = probesVisitor;
49 		this.idGenerator = idGenerator;
50 		this.tryCatchProbeLabels = new HashMap<Label, Label>();
51 	}
52 
53 	/**
54 	 * If an analyzer is set {@link IFrame} handles are calculated and emitted
55 	 * to the probes methods.
56 	 *
57 	 * @param analyzer
58 	 *            optional analyzer to set
59 	 */
setAnalyzer(final AnalyzerAdapter analyzer)60 	public void setAnalyzer(final AnalyzerAdapter analyzer) {
61 		this.analyzer = analyzer;
62 	}
63 
64 	@Override
visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type)65 	public void visitTryCatchBlock(final Label start, final Label end,
66 			final Label handler, final String type) {
67 		probesVisitor.visitTryCatchBlock(getTryCatchLabel(start), getTryCatchLabel(end),
68 				handler, type);
69 	}
70 
getTryCatchLabel(Label label)71 	private Label getTryCatchLabel(Label label) {
72 		if (tryCatchProbeLabels.containsKey(label)) {
73 			label = tryCatchProbeLabels.get(label);
74 		} else if (LabelInfo.needsProbe(label)) {
75 			// If a probe will be inserted before the label, we'll need to use a
76 			// different label to define the range of the try-catch block.
77 			final Label probeLabel = new Label();
78 			LabelInfo.setSuccessor(probeLabel);
79 			tryCatchProbeLabels.put(label, probeLabel);
80 			label = probeLabel;
81 		}
82 		return label;
83 	}
84 
85 	@Override
visitLabel(final Label label)86 	public void visitLabel(final Label label) {
87 		if (LabelInfo.needsProbe(label)) {
88 			if (tryCatchProbeLabels.containsKey(label)) {
89 				probesVisitor.visitLabel(tryCatchProbeLabels.get(label));
90 			}
91 			probesVisitor.visitProbe(idGenerator.nextId());
92 		}
93 		probesVisitor.visitLabel(label);
94 	}
95 
96 	@Override
visitInsn(final int opcode)97 	public void visitInsn(final int opcode) {
98 		switch (opcode) {
99 		case Opcodes.IRETURN:
100 		case Opcodes.LRETURN:
101 		case Opcodes.FRETURN:
102 		case Opcodes.DRETURN:
103 		case Opcodes.ARETURN:
104 		case Opcodes.RETURN:
105 		case Opcodes.ATHROW:
106 			probesVisitor.visitInsnWithProbe(opcode, idGenerator.nextId());
107 			break;
108 		default:
109 			probesVisitor.visitInsn(opcode);
110 			break;
111 		}
112 	}
113 
114 	@Override
visitJumpInsn(final int opcode, final Label label)115 	public void visitJumpInsn(final int opcode, final Label label) {
116 		if (LabelInfo.isMultiTarget(label)) {
117 			probesVisitor.visitJumpInsnWithProbe(opcode, label,
118 					idGenerator.nextId(), frame(jumpPopCount(opcode)));
119 		} else {
120 			probesVisitor.visitJumpInsn(opcode, label);
121 		}
122 	}
123 
jumpPopCount(final int opcode)124 	private int jumpPopCount(final int opcode) {
125 		switch (opcode) {
126 		case Opcodes.GOTO:
127 			return 0;
128 		case Opcodes.IFEQ:
129 		case Opcodes.IFNE:
130 		case Opcodes.IFLT:
131 		case Opcodes.IFGE:
132 		case Opcodes.IFGT:
133 		case Opcodes.IFLE:
134 		case Opcodes.IFNULL:
135 		case Opcodes.IFNONNULL:
136 			return 1;
137 		default: // IF_CMPxx and IF_ACMPxx
138 			return 2;
139 		}
140 	}
141 
142 	@Override
visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels)143 	public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
144 			final Label[] labels) {
145 		if (markLabels(dflt, labels)) {
146 			probesVisitor.visitLookupSwitchInsnWithProbes(dflt, keys, labels,
147 					frame(1));
148 		} else {
149 			probesVisitor.visitLookupSwitchInsn(dflt, keys, labels);
150 		}
151 	}
152 
153 	@Override
visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels)154 	public void visitTableSwitchInsn(final int min, final int max,
155 			final Label dflt, final Label... labels) {
156 		if (markLabels(dflt, labels)) {
157 			probesVisitor.visitTableSwitchInsnWithProbes(min, max, dflt,
158 					labels, frame(1));
159 		} else {
160 			probesVisitor.visitTableSwitchInsn(min, max, dflt, labels);
161 		}
162 	}
163 
markLabels(final Label dflt, final Label[] labels)164 	private boolean markLabels(final Label dflt, final Label[] labels) {
165 		boolean probe = false;
166 		LabelInfo.resetDone(labels);
167 		if (LabelInfo.isMultiTarget(dflt)) {
168 			LabelInfo.setProbeId(dflt, idGenerator.nextId());
169 			probe = true;
170 		}
171 		LabelInfo.setDone(dflt);
172 		for (final Label l : labels) {
173 			if (LabelInfo.isMultiTarget(l) && !LabelInfo.isDone(l)) {
174 				LabelInfo.setProbeId(l, idGenerator.nextId());
175 				probe = true;
176 			}
177 			LabelInfo.setDone(l);
178 		}
179 		return probe;
180 	}
181 
frame(final int popCount)182 	private IFrame frame(final int popCount) {
183 		return FrameSnapshot.create(analyzer, popCount);
184 	}
185 
186 }
187