/******************************************************************************* * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Marc R. Hoffmann - initial API and implementation * *******************************************************************************/ package org.jacoco.core.internal.instr; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; /** * Internal utility to add probes into the control flow of a method. The code * for a probe simply sets a certain slot of a boolean array to true. In * addition the probe array has to be retrieved at the beginning of the method * and stored in a local variable. */ class ProbeInserter extends MethodVisitor implements IProbeInserter { private final IProbeArrayStrategy arrayStrategy; /** * true if method is a class or interface initialization * method. */ private final boolean clinit; /** Position of the inserted variable. */ private final int variable; /** Maximum stack usage of the code to access the probe array. */ private int accessorStackSize; /** * Creates a new {@link ProbeInserter}. * * @param access * access flags of the adapted method * @param name * the method's name * @param desc * the method's descriptor * @param mv * the method visitor to which this adapter delegates calls * @param arrayStrategy * callback to create the code that retrieves the reference to * the probe array */ ProbeInserter(final int access, final String name, final String desc, final MethodVisitor mv, final IProbeArrayStrategy arrayStrategy) { super(InstrSupport.ASM_API_VERSION, mv); this.clinit = InstrSupport.CLINIT_NAME.equals(name); this.arrayStrategy = arrayStrategy; int pos = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0; for (final Type t : Type.getArgumentTypes(desc)) { pos += t.getSize(); } variable = pos; } public void insertProbe(final int id) { // For a probe we set the corresponding position in the boolean[] array // to true. mv.visitVarInsn(Opcodes.ALOAD, variable); // Stack[0]: [Z InstrSupport.push(mv, id); // Stack[1]: I // Stack[0]: [Z mv.visitInsn(Opcodes.ICONST_1); // Stack[2]: I // Stack[1]: I // Stack[0]: [Z mv.visitInsn(Opcodes.BASTORE); } @Override public void visitCode() { accessorStackSize = arrayStrategy.storeInstance(mv, clinit, variable); mv.visitCode(); } @Override public final void visitVarInsn(final int opcode, final int var) { mv.visitVarInsn(opcode, map(var)); } @Override public final void visitIincInsn(final int var, final int increment) { mv.visitIincInsn(map(var), increment); } @Override public final void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, final int index) { mv.visitLocalVariable(name, desc, signature, start, end, map(index)); } @Override public void visitMaxs(final int maxStack, final int maxLocals) { // Max stack size of the probe code is 3 which can add to the // original stack size depending on the probe locations. The accessor // stack size is an absolute maximum, as the accessor code is inserted // at the very beginning of each method when the stack size is empty. final int increasedStack = Math.max(maxStack + 3, accessorStackSize); mv.visitMaxs(increasedStack, maxLocals + 1); } private int map(final int var) { if (var < variable) { return var; } else { return var + 1; } } @Override public final void visitFrame(final int type, final int nLocal, final Object[] local, final int nStack, final Object[] stack) { if (type != Opcodes.F_NEW) { // uncompressed frame throw new IllegalArgumentException( "ClassReader.accept() should be called with EXPAND_FRAMES flag"); } final Object[] newLocal = new Object[Math.max(nLocal, variable) + 1]; int idx = 0; // Arrays index for existing locals int newIdx = 0; // Array index for new locals int pos = 0; // Current variable position while (idx < nLocal || pos <= variable) { if (pos == variable) { newLocal[newIdx++] = InstrSupport.DATAFIELD_DESC; pos++; } else { if (idx < nLocal) { final Object t = local[idx++]; newLocal[newIdx++] = t; pos++; if (t == Opcodes.LONG || t == Opcodes.DOUBLE) { pos++; } } else { // Fill unused slots with TOP newLocal[newIdx++] = Opcodes.TOP; pos++; } } } mv.visitFrame(type, newIdx, newLocal, nStack, stack); } }