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