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