• 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.instr;
14 
15 import org.jacoco.core.internal.flow.IFrame;
16 import org.jacoco.core.internal.flow.LabelInfo;
17 import org.jacoco.core.internal.flow.MethodProbesVisitor;
18 import org.objectweb.asm.Label;
19 import org.objectweb.asm.MethodVisitor;
20 import org.objectweb.asm.Opcodes;
21 
22 /**
23  * This method adapter inserts probes as requested by the
24  * {@link MethodProbesVisitor} events.
25  */
26 class MethodInstrumenter extends MethodProbesVisitor {
27 
28 	private final IProbeInserter probeInserter;
29 
30 	/**
31 	 * Create a new instrumenter instance for the given method.
32 	 *
33 	 * @param mv
34 	 *            next method visitor in the chain
35 	 * @param probeInserter
36 	 *            call-back to insert probes where required
37 	 */
MethodInstrumenter(final MethodVisitor mv, final IProbeInserter probeInserter)38 	public MethodInstrumenter(final MethodVisitor mv,
39 			final IProbeInserter probeInserter) {
40 		super(mv);
41 		this.probeInserter = probeInserter;
42 	}
43 
44 	// === IMethodProbesVisitor ===
45 
46 	@Override
visitProbe(final int probeId)47 	public void visitProbe(final int probeId) {
48 		probeInserter.insertProbe(probeId);
49 	}
50 
51 	@Override
visitInsnWithProbe(final int opcode, final int probeId)52 	public void visitInsnWithProbe(final int opcode, final int probeId) {
53 		probeInserter.insertProbe(probeId);
54 		mv.visitInsn(opcode);
55 	}
56 
57 	@Override
visitJumpInsnWithProbe(final int opcode, final Label label, final int probeId, final IFrame frame)58 	public void visitJumpInsnWithProbe(final int opcode, final Label label,
59 			final int probeId, final IFrame frame) {
60 		if (opcode == Opcodes.GOTO) {
61 			probeInserter.insertProbe(probeId);
62 			mv.visitJumpInsn(Opcodes.GOTO, label);
63 		} else {
64 			final Label intermediate = new Label();
65 			mv.visitJumpInsn(getInverted(opcode), intermediate);
66 			probeInserter.insertProbe(probeId);
67 			mv.visitJumpInsn(Opcodes.GOTO, label);
68 			mv.visitLabel(intermediate);
69 			frame.accept(mv);
70 		}
71 	}
72 
getInverted(final int opcode)73 	private int getInverted(final int opcode) {
74 		switch (opcode) {
75 		case Opcodes.IFEQ:
76 			return Opcodes.IFNE;
77 		case Opcodes.IFNE:
78 			return Opcodes.IFEQ;
79 		case Opcodes.IFLT:
80 			return Opcodes.IFGE;
81 		case Opcodes.IFGE:
82 			return Opcodes.IFLT;
83 		case Opcodes.IFGT:
84 			return Opcodes.IFLE;
85 		case Opcodes.IFLE:
86 			return Opcodes.IFGT;
87 		case Opcodes.IF_ICMPEQ:
88 			return Opcodes.IF_ICMPNE;
89 		case Opcodes.IF_ICMPNE:
90 			return Opcodes.IF_ICMPEQ;
91 		case Opcodes.IF_ICMPLT:
92 			return Opcodes.IF_ICMPGE;
93 		case Opcodes.IF_ICMPGE:
94 			return Opcodes.IF_ICMPLT;
95 		case Opcodes.IF_ICMPGT:
96 			return Opcodes.IF_ICMPLE;
97 		case Opcodes.IF_ICMPLE:
98 			return Opcodes.IF_ICMPGT;
99 		case Opcodes.IF_ACMPEQ:
100 			return Opcodes.IF_ACMPNE;
101 		case Opcodes.IF_ACMPNE:
102 			return Opcodes.IF_ACMPEQ;
103 		case Opcodes.IFNULL:
104 			return Opcodes.IFNONNULL;
105 		case Opcodes.IFNONNULL:
106 			return Opcodes.IFNULL;
107 		}
108 		throw new IllegalArgumentException();
109 	}
110 
111 	@Override
visitTableSwitchInsnWithProbes(final int min, final int max, final Label dflt, final Label[] labels, final IFrame frame)112 	public void visitTableSwitchInsnWithProbes(final int min, final int max,
113 			final Label dflt, final Label[] labels, final IFrame frame) {
114 		// 1. Calculate intermediate labels:
115 		LabelInfo.resetDone(dflt);
116 		LabelInfo.resetDone(labels);
117 		final Label newDflt = createIntermediate(dflt);
118 		final Label[] newLabels = createIntermediates(labels);
119 		mv.visitTableSwitchInsn(min, max, newDflt, newLabels);
120 
121 		// 2. Insert probes:
122 		insertIntermediateProbes(dflt, labels, frame);
123 	}
124 
125 	@Override
visitLookupSwitchInsnWithProbes(final Label dflt, final int[] keys, final Label[] labels, final IFrame frame)126 	public void visitLookupSwitchInsnWithProbes(final Label dflt,
127 			final int[] keys, final Label[] labels, final IFrame frame) {
128 		// 1. Calculate intermediate labels:
129 		LabelInfo.resetDone(dflt);
130 		LabelInfo.resetDone(labels);
131 		final Label newDflt = createIntermediate(dflt);
132 		final Label[] newLabels = createIntermediates(labels);
133 		mv.visitLookupSwitchInsn(newDflt, keys, newLabels);
134 
135 		// 2. Insert probes:
136 		insertIntermediateProbes(dflt, labels, frame);
137 	}
138 
createIntermediates(final Label[] labels)139 	private Label[] createIntermediates(final Label[] labels) {
140 		final Label[] intermediates = new Label[labels.length];
141 		for (int i = 0; i < labels.length; i++) {
142 			intermediates[i] = createIntermediate(labels[i]);
143 		}
144 		return intermediates;
145 	}
146 
createIntermediate(final Label label)147 	private Label createIntermediate(final Label label) {
148 		final Label intermediate;
149 		if (LabelInfo.getProbeId(label) == LabelInfo.NO_PROBE) {
150 			intermediate = label;
151 		} else {
152 			if (LabelInfo.isDone(label)) {
153 				intermediate = LabelInfo.getIntermediateLabel(label);
154 			} else {
155 				intermediate = new Label();
156 				LabelInfo.setIntermediateLabel(label, intermediate);
157 				LabelInfo.setDone(label);
158 			}
159 		}
160 		return intermediate;
161 	}
162 
insertIntermediateProbe(final Label label, final IFrame frame)163 	private void insertIntermediateProbe(final Label label,
164 			final IFrame frame) {
165 		final int probeId = LabelInfo.getProbeId(label);
166 		if (probeId != LabelInfo.NO_PROBE && !LabelInfo.isDone(label)) {
167 			mv.visitLabel(LabelInfo.getIntermediateLabel(label));
168 			frame.accept(mv);
169 			probeInserter.insertProbe(probeId);
170 			mv.visitJumpInsn(Opcodes.GOTO, label);
171 			LabelInfo.setDone(label);
172 		}
173 	}
174 
insertIntermediateProbes(final Label dflt, final Label[] labels, final IFrame frame)175 	private void insertIntermediateProbes(final Label dflt,
176 			final Label[] labels, final IFrame frame) {
177 		LabelInfo.resetDone(dflt);
178 		LabelInfo.resetDone(labels);
179 		insertIntermediateProbe(dflt, frame);
180 		for (final Label l : labels) {
181 			insertIntermediateProbe(l, frame);
182 		}
183 	}
184 
185 }
186