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.objectweb.asm.AnnotationVisitor; 16 import org.objectweb.asm.Label; 17 import org.objectweb.asm.MethodVisitor; 18 import org.objectweb.asm.Opcodes; 19 import org.objectweb.asm.Type; 20 import org.objectweb.asm.TypePath; 21 22 /** 23 * Internal utility to add probes into the control flow of a method. The code 24 * for a probe simply sets a certain slot of a boolean array to true. In 25 * addition the probe array has to be retrieved at the beginning of the method 26 * and stored in a local variable. 27 */ 28 class ProbeInserter extends MethodVisitor implements IProbeInserter { 29 30 private final IProbeArrayStrategy arrayStrategy; 31 32 /** 33 * <code>true</code> if method is a class or interface initialization 34 * method. 35 */ 36 private final boolean clinit; 37 38 /** Position of the inserted variable. */ 39 private final int variable; 40 41 /** Maximum stack usage of the code to access the probe array. */ 42 private int accessorStackSize; 43 44 /** 45 * Creates a new {@link ProbeInserter}. 46 * 47 * @param access 48 * access flags of the adapted method 49 * @param name 50 * the method's name 51 * @param desc 52 * the method's descriptor 53 * @param mv 54 * the method visitor to which this adapter delegates calls 55 * @param arrayStrategy 56 * callback to create the code that retrieves the reference to 57 * the probe array 58 */ ProbeInserter(final int access, final String name, final String desc, final MethodVisitor mv, final IProbeArrayStrategy arrayStrategy)59 ProbeInserter(final int access, final String name, final String desc, 60 final MethodVisitor mv, final IProbeArrayStrategy arrayStrategy) { 61 super(InstrSupport.ASM_API_VERSION, mv); 62 this.clinit = InstrSupport.CLINIT_NAME.equals(name); 63 this.arrayStrategy = arrayStrategy; 64 int pos = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0; 65 for (final Type t : Type.getArgumentTypes(desc)) { 66 pos += t.getSize(); 67 } 68 variable = pos; 69 } 70 insertProbe(final int id)71 public void insertProbe(final int id) { 72 73 // BEGIN android-change 74 // For a probe we call setProbe on the IExecutionData object. 75 76 mv.visitVarInsn(Opcodes.ALOAD, variable); 77 78 // Stack[0]: Lorg/jacoco/core/data/IExecutionData; 79 80 InstrSupport.push(mv, id); 81 82 // Stack[1]: I 83 // Stack[0]: Lorg/jacoco/core/data/IExecutionData; 84 85 mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "org/jacoco/core/data/IExecutionData", "setProbe", "(I)V", true); 86 // END android-change 87 } 88 89 @Override visitCode()90 public void visitCode() { 91 accessorStackSize = arrayStrategy.storeInstance(mv, clinit, variable); 92 mv.visitCode(); 93 } 94 95 @Override visitVarInsn(final int opcode, final int var)96 public final void visitVarInsn(final int opcode, final int var) { 97 mv.visitVarInsn(opcode, map(var)); 98 } 99 100 @Override visitIincInsn(final int var, final int increment)101 public final void visitIincInsn(final int var, final int increment) { 102 mv.visitIincInsn(map(var), increment); 103 } 104 105 @Override visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, final int index)106 public final void visitLocalVariable(final String name, final String desc, 107 final String signature, final Label start, final Label end, 108 final int index) { 109 mv.visitLocalVariable(name, desc, signature, start, end, map(index)); 110 } 111 112 @Override visitLocalVariableAnnotation(final int typeRef, final TypePath typePath, final Label[] start, final Label[] end, final int[] index, final String descriptor, final boolean visible)113 public AnnotationVisitor visitLocalVariableAnnotation(final int typeRef, 114 final TypePath typePath, final Label[] start, final Label[] end, 115 final int[] index, final String descriptor, final boolean visible) { 116 final int[] newIndex = new int[index.length]; 117 for (int i = 0; i < newIndex.length; i++) { 118 newIndex[i] = map(index[i]); 119 } 120 return mv.visitLocalVariableAnnotation(typeRef, typePath, start, end, 121 newIndex, descriptor, visible); 122 } 123 124 @Override visitMaxs(final int maxStack, final int maxLocals)125 public void visitMaxs(final int maxStack, final int maxLocals) { 126 // Max stack size of the probe code is 3 which can add to the 127 // original stack size depending on the probe locations. The accessor 128 // stack size is an absolute maximum, as the accessor code is inserted 129 // at the very beginning of each method when the stack size is empty. 130 final int increasedStack = Math.max(maxStack + 3, accessorStackSize); 131 mv.visitMaxs(increasedStack, maxLocals + 1); 132 } 133 map(final int var)134 private int map(final int var) { 135 if (var < variable) { 136 return var; 137 } else { 138 return var + 1; 139 } 140 } 141 142 @Override visitFrame(final int type, final int nLocal, final Object[] local, final int nStack, final Object[] stack)143 public final void visitFrame(final int type, final int nLocal, 144 final Object[] local, final int nStack, final Object[] stack) { 145 146 if (type != Opcodes.F_NEW) { // uncompressed frame 147 throw new IllegalArgumentException( 148 "ClassReader.accept() should be called with EXPAND_FRAMES flag"); 149 } 150 151 final Object[] newLocal = new Object[Math.max(nLocal, variable) + 1]; 152 int idx = 0; // Arrays index for existing locals 153 int newIdx = 0; // Array index for new locals 154 int pos = 0; // Current variable position 155 while (idx < nLocal || pos <= variable) { 156 if (pos == variable) { 157 newLocal[newIdx++] = InstrSupport.DATAFIELD_DESC; 158 pos++; 159 } else { 160 if (idx < nLocal) { 161 final Object t = local[idx++]; 162 newLocal[newIdx++] = t; 163 pos++; 164 if (t == Opcodes.LONG || t == Opcodes.DOUBLE) { 165 pos++; 166 } 167 } else { 168 // Fill unused slots with TOP 169 newLocal[newIdx++] = Opcodes.TOP; 170 pos++; 171 } 172 } 173 } 174 mv.visitFrame(type, newIdx, newLocal, nStack, stack); 175 } 176 177 } 178