/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.whip.instrument;
import com.liferay.whip.coveragedata.TouchUtil;
import java.util.Map;
import java.util.Set;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.MethodNode;
/**
* @author Shuyang Zhou
*/
public class TouchMethodVisitor extends MethodVisitor {
public TouchMethodVisitor(
String owner, MethodNode methodNode, MethodVisitor methodVisitor,
Set<Label> jumpLabels, Map<Label, Integer> lineLabels,
Map<Label, SwitchHolder> switchLabels) {
super(Opcodes.ASM5, methodVisitor);
_owner = owner;
_jumpLabels = jumpLabels;
_lineLabels = lineLabels;
_switchLabels = switchLabels;
int variableCount = 0;
if ((Opcodes.ACC_STATIC & methodNode.access) == 0) {
variableCount++;
}
for (Type type : Type.getArgumentTypes(methodNode.desc)) {
variableCount += type.getSize();
}
_variableCount = variableCount;
}
@Override
public void visitCode() {
_started = true;
super.visitCode();
}
@Override
public void visitFieldInsn(
int opcode, String owner, String name, String desc) {
_touchBranch();
super.visitFieldInsn(opcode, owner, name, desc);
}
@Override
public void visitIincInsn(int var, int increment) {
_touchBranch();
if (var >= _variableCount) {
var += 2;
}
mv.visitIincInsn(var, increment);
}
@Override
public void visitInsn(int opcode) {
_touchBranch();
super.visitInsn(opcode);
}
@Override
public void visitIntInsn(int opcode, int operand) {
_touchBranch();
super.visitIntInsn(opcode, operand);
}
@Override
public void visitJumpInsn(int opcode, Label label) {
_touchBranch();
if ((_currentLine != 0) && (opcode != Opcodes.GOTO) &&
(opcode != Opcodes.JSR)) {
mv.visitIntInsn(Opcodes.SIPUSH, _currentLine);
mv.visitVarInsn(Opcodes.ISTORE, _variableIndex);
mv.visitIntInsn(Opcodes.SIPUSH, _currentJump);
mv.visitVarInsn(Opcodes.ISTORE, _variableIndex + 1);
_lastJump = new JumpHolder(_currentLine, _currentJump++);
}
super.visitJumpInsn(opcode, label);
}
@Override
public void visitLabel(Label label) {
if (_started) {
_started = false;
if (!_jumpLabels.isEmpty()) {
_variableIndex = _variableCount;
mv.visitInsn(Opcodes.ICONST_0);
mv.visitVarInsn(Opcodes.ISTORE, _variableIndex);
mv.visitIntInsn(Opcodes.SIPUSH, -1);
mv.visitVarInsn(Opcodes.ISTORE, _variableIndex + 1);
_startLabel = label;
}
}
_endLabel = label;
super.visitLabel(label);
if (_jumpLabels.contains(label)) {
if (_lastJump != null) {
Label label1 = _lastJump();
_touchJump(false);
Label label2 = new Label();
mv.visitJumpInsn(Opcodes.GOTO, label2);
mv.visitLabel(label1);
mv.visitVarInsn(Opcodes.ILOAD, _variableIndex + 1);
mv.visitJumpInsn(Opcodes.IFLT, label2);
_touchJump(true);
mv.visitLabel(label2);
}
else {
mv.visitVarInsn(Opcodes.ILOAD, _variableIndex + 1);
Label label1 = new Label();
mv.visitJumpInsn(Opcodes.IFLT, label1);
_touchJump(true);
mv.visitLabel(label1);
}
}
else if (_lastJump != null) {
Label label1 = _lastJump();
_touchJump(false);
mv.visitLabel(label1);
}
_lastJump = null;
SwitchHolder switchHolder = _switchLabels.get(label);
if (switchHolder != null) {
_touchSwitch(
switchHolder.getLineNumber(), switchHolder.getJumpNumber(),
switchHolder.getBranch());
}
Integer line = _lineLabels.get(label);
if (line != null) {
visitLineNumber(line, label);
}
}
@Override
public void visitLdcInsn(Object cst) {
_touchBranch();
super.visitLdcInsn(cst);
}
@Override
public void visitLineNumber(int line, Label start) {
_currentLine = line;
_currentJump = 0;
mv.visitLdcInsn(_owner);
mv.visitIntInsn(Opcodes.SIPUSH, line);
mv.visitMethodInsn(
Opcodes.INVOKESTATIC, _TOUCH_UTIL_CLASS, "touch",
"(Ljava/lang/String;I)V", false);
super.visitLineNumber(line, start);
}
@Override
public void visitLocalVariable(
String name, String desc, String signature, Label start, Label end,
int index) {
if (index >= _variableCount) {
index += 2;
}
mv.visitLocalVariable(name, desc, signature, start, end, index);
}
@Override
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
_touchBranch();
super.visitLookupSwitchInsn(dflt, keys, labels);
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
if (!_jumpLabels.isEmpty()) {
mv.visitLocalVariable(
"__whip__line__number__", "I", null, _startLabel, _endLabel,
_variableIndex);
mv.visitLocalVariable(
"__whip__branch__number__", "I", null, _startLabel, _endLabel,
_variableIndex + 1);
}
super.visitMaxs(maxStack, maxLocals);
}
@Override
public void visitMethodInsn(
int opcode, String owner, String name, String desc) {
visitMethodInsn(opcode, owner, name, desc, false);
}
@Override
public void visitMethodInsn(
int opcode, String owner, String name, String desc, boolean itf) {
_touchBranch();
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
@Override
public void visitMultiANewArrayInsn(String desc, int dims) {
_touchBranch();
super.visitMultiANewArrayInsn(desc, dims);
}
@Override
public void visitTableSwitchInsn(
int min, int max, Label dflt, Label... labels) {
_touchBranch();
super.visitTableSwitchInsn(min, max, dflt, labels);
}
@Override
public void visitTryCatchBlock(
Label start, Label end, Label handler, String type) {
_touchBranch();
super.visitTryCatchBlock(start, end, handler, type);
}
@Override
public void visitTypeInsn(int opcode, String desc) {
_touchBranch();
super.visitTypeInsn(opcode, desc);
}
@Override
public void visitVarInsn(int opcode, int var) {
_touchBranch();
if (var >= _variableCount) {
var += 2;
}
mv.visitVarInsn(opcode, var);
}
private Label _lastJump() {
mv.visitVarInsn(Opcodes.ILOAD, _variableIndex);
mv.visitIntInsn(Opcodes.SIPUSH, _lastJump.getLineNumber());
Label label = new Label();
mv.visitJumpInsn(Opcodes.IF_ICMPNE, label);
mv.visitVarInsn(Opcodes.ILOAD, _variableIndex + 1);
mv.visitIntInsn(Opcodes.SIPUSH, _lastJump.getJumpNumber());
mv.visitJumpInsn(Opcodes.IF_ICMPNE, label);
return label;
}
private void _touchBranch() {
if (_lastJump != null) {
_lastJump = null;
_touchJump(false);
}
}
private void _touchJump(boolean branch) {
mv.visitLdcInsn(_owner);
mv.visitVarInsn(Opcodes.ILOAD, _variableIndex);
mv.visitVarInsn(Opcodes.ILOAD, _variableIndex + 1);
if (branch) {
mv.visitInsn(Opcodes.ICONST_0);
}
else {
mv.visitInsn(Opcodes.ICONST_1);
}
mv.visitMethodInsn(
Opcodes.INVOKESTATIC, _TOUCH_UTIL_CLASS, "touchJump",
"(Ljava/lang/String;IIZ)V", false);
mv.visitIntInsn(Opcodes.SIPUSH, -1);
mv.visitVarInsn(Opcodes.ISTORE, _variableIndex + 1);
}
private void _touchSwitch(int lineNumber, int switchNumber, int branch) {
mv.visitLdcInsn(_owner);
mv.visitIntInsn(Opcodes.SIPUSH, lineNumber);
mv.visitIntInsn(Opcodes.SIPUSH, switchNumber);
mv.visitIntInsn(Opcodes.SIPUSH, branch);
mv.visitMethodInsn(
Opcodes.INVOKESTATIC, _TOUCH_UTIL_CLASS, "touchSwitch",
"(Ljava/lang/String;III)V", false);
}
private static final String _TOUCH_UTIL_CLASS = Type.getInternalName(
TouchUtil.class);
private int _currentJump;
private int _currentLine;
private Label _endLabel;
private final Set<Label> _jumpLabels;
private JumpHolder _lastJump;
private final Map<Label, Integer> _lineLabels;
private final String _owner;
private boolean _started;
private Label _startLabel;
private final Map<Label, SwitchHolder> _switchLabels;
private final int _variableCount;
private int _variableIndex;
}