Java tutorial
/******************************************************************************* * Copyright (c) 2009, 2012 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 java.util.HashMap; import java.util.Map; import org.jacoco.core.internal.flow.LabelInfo; import org.objectweb.asm.Label; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.FrameNode; /** * Internal utility to add a local variable for the probe array at the beginning * of every method. The adapter adjusts all other local variables and frames * accordingly. In addition the adapter records the frames at certain labels to * re-insert them if required for jump probes. * * Basically this is a simplified version of ASM's * {@link org.objectweb.asm.commons.LocalVariablesSorter} to avoid expensive * mapping tables and prevents ASM bug #314563. */ class ProbeVariableInserter extends MethodAdapter { /** Position of the inserted variable. */ protected final int variable; /** Index the inserted variable. */ private final int variableIdx; private boolean firstFrame = true; private Label lastLabel; private Map<Label, FrameNode> probeFrames; /** * Creates a new {@link ProbeVariableInserter}. * * @param access * access flags of the adapted method. * @param desc * the method's descriptor * @param mv * the method visitor to which this adapter delegates calls */ ProbeVariableInserter(final int access, final String desc, final MethodVisitor mv) { super(mv); int idx = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0; int pos = idx; for (final Type t : Type.getArgumentTypes(desc)) { idx++; pos += t.getSize(); } variableIdx = idx; variable = pos; lastLabel = null; } @Override public void visitLabel(final Label label) { mv.visitLabel(label); lastLabel = label; } @Override public void visitVarInsn(final int opcode, final int var) { mv.visitVarInsn(opcode, map(var)); } @Override public void visitIincInsn(final int var, final int increment) { mv.visitIincInsn(map(var), increment); } @Override public void visitMaxs(final int maxStack, final int maxLocals) { mv.visitMaxs(maxStack, maxLocals + 1); } @Override public 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)); } private int map(final int var) { if (var < variable) { return var; } else { return var + 1; } } @Override public 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 IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag"); } final Object[] newLocal = new Object[nLocal + 1]; for (int i = 0; i <= local.length; i++) { if (i < variableIdx) { newLocal[i] = local[i]; continue; } if (i > variableIdx) { newLocal[i] = local[i - 1]; continue; } newLocal[i] = InstrSupport.DATAFIELD_DESC; } if (lastLabel != null) { if (LabelInfo.isMultiTarget(lastLabel)) { // Create map instance only if required: if (probeFrames == null) { probeFrames = new HashMap<Label, FrameNode>(); } probeFrames.put(lastLabel, new FrameNode(type, nLocal + 1, newLocal, nStack, stack)); } lastLabel = null; } if (firstFrame) { // The first frame is generated by ASM and represents the implicit // frame derived from the method signature only. This frame must not // yet be modified. mv.visitFrame(type, nLocal, local, nStack, stack); firstFrame = false; } else { mv.visitFrame(type, nLocal + 1, newLocal, nStack, stack); } } /** * Inserts the frame again that was inserted after the given label. * * @param label * label of the frame to insert */ protected void insertProbeFrame(final Label label) { if (probeFrames != null) { final FrameNode frame = probeFrames.get(label); if (frame != null) { frame.accept(mv); } } } }