/*
* JCarder -- cards Java programs to keep threads disentangled
*
* Copyright (C) 2006-2007 Enea AB
* Copyright (C) 2007 Ulrik Svensson
* Copyright (C) 2007 Joel Rosdahl
*
* This program is made available under the GNU GPL version 2, with a special
* exception for linking with JUnit. See the accompanying file LICENSE.txt for
* details.
*
* This program 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.
*/
package com.enea.jcarder.agent.instrument;
import java.util.Stack;
import net.jcip.annotations.NotThreadSafe;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import com.enea.jcarder.util.logging.Logger;
/**
* This class tries to keep track of what is currently on the operand stack. It
* does not keep track of the actual values but from where the values
* originates. A value may for example originate from a specific field member in
* the class, a local variable, a return value from a specific method or
* something else.
*
* The analysis is done during the instrumentation.
*/
@NotThreadSafe
class StackAnalyzeMethodVisitor implements MethodVisitor {
private static final TextualDescription UNKOWN_VALUE =
new TextualDescription("???");
private final Logger mLogger;
private final Stack<Object> mStack = new Stack<Object>();
private final MethodVisitor mMethodVisitor;
private final boolean mIsStatic;
StackAnalyzeMethodVisitor(final Logger logger,
final MethodVisitor methodVisitor,
final boolean isStatic) {
mLogger = logger;
mMethodVisitor = methodVisitor;
mIsStatic = isStatic;
}
private static class TextualDescription {
private final String mDescription;
TextualDescription(String description) {
mDescription = description;
}
public String toString() {
return mDescription;
}
}
/**
* @return A textual description of from where the current value of the
* stack originates. The string "???" is returned if the origin is
* unknown.
*/
String peek() {
if (mStack.isEmpty()) {
return UNKOWN_VALUE.toString();
} else {
return mStack.peek().toString();
}
}
private String pop() {
return popObject().toString();
}
private Object popObject() {
if (mStack.isEmpty()) {
return UNKOWN_VALUE;
} else {
return mStack.pop();
}
}
private void pushTextualDescription(String s) {
mStack.push(new TextualDescription(s));
}
private void pushStringObject(String s) {
mStack.push(s);
}
private void clear() {
mLogger.finest("Invalidating stack");
mStack.clear();
}
public void visitCode() {
mMethodVisitor.visitCode();
clear();
}
public void visitEnd() {
mMethodVisitor.visitEnd();
clear();
}
public void visitFieldInsn(int opCode,
String owner,
String name,
String desc) {
mMethodVisitor.visitFieldInsn(opCode, owner, name, desc);
switch (opCode) {
case Opcodes.GETFIELD:
pop();
pushTextualDescription(owner + "." + name);
break;
case Opcodes.GETSTATIC:
pushTextualDescription(owner + "." + name);
break;
default:
clear();
}
}
public void visitIincInsn(int arg0, int arg1) {
mMethodVisitor.visitIincInsn(arg0, arg1);
clear();
}
public void visitInsn(int opCode) {
mMethodVisitor.visitInsn(opCode);
switch (opCode) {
case Opcodes.DUP:
pushTextualDescription(peek());
break;
default:
clear();
}
}
public void visitIntInsn(int opCode, int arg1) {
mMethodVisitor.visitIntInsn(opCode, arg1);
clear();
}
public void visitJumpInsn(int opCode, Label arg1) {
mMethodVisitor.visitJumpInsn(opCode, arg1);
clear();
}
public void visitLabel(Label arg0) {
mMethodVisitor.visitLabel(arg0);
// We have to invalidate the stack since we don't know how we arrived
// at this label. We might have jumped to this place from anywhere.
clear();
}
public void visitLdcInsn(Object cst) {
mMethodVisitor.visitLdcInsn(cst);
if (cst instanceof Type) {
Type t = (Type) cst;
pushTextualDescription(t.getClassName() + ".class");
} else if (cst instanceof String) {
pushStringObject((String) cst);
} else {
clear();
}
}
public void visitLineNumber(int arg0, Label arg1) {
mMethodVisitor.visitLineNumber(arg0, arg1);
}
public void visitLocalVariable(String name,
String desc,
String signature,
Label start,
Label end,
int index) {
mMethodVisitor.visitLocalVariable(name,
desc,
signature,
start,
end,
index);
}
public void visitLookupSwitchInsn(Label arg0, int[] arg1, Label[] arg2) {
mMethodVisitor.visitLookupSwitchInsn(arg0, arg1, arg2);
clear();
}
// TODO refactor this method
public void visitMethodInsn(int opCode,
String owner,
String name,
String desc) {
mMethodVisitor.visitMethodInsn(opCode, owner, name, desc);
switch (opCode) {
case Opcodes.INVOKEVIRTUAL:
// pass through to next case.
case Opcodes.INVOKESPECIAL:
// pass through to next case.
case Opcodes.INVOKESTATIC:
if ("forName".equals(name)
&& "java/lang/Class".equals(owner)
&& "(Ljava/lang/String;)Ljava/lang/Class;".equals(desc)) {
Object stackObject = popObject();
if (stackObject instanceof String) {
String classDescription = ((String) stackObject) + ".class";
pushTextualDescription(classDescription);
break;
}
}
// pass through to next case.
case Opcodes.INVOKEINTERFACE:
clear();
if (isNonVoidMethod(name, desc)) {
pushTextualDescription(owner + "." + name + "()");
}
break;
default:
clear();
}
}
private static boolean isNonVoidMethod(String name, String desc) {
return Type.getReturnType(desc) != Type.VOID_TYPE
|| name.equals("<init>");
}
public void visitMultiANewArrayInsn(String arg0, int arg1) {
mMethodVisitor.visitMultiANewArrayInsn(arg0, arg1);
clear();
}
public AnnotationVisitor visitParameterAnnotation(int arg0,
String arg1,
boolean arg2) {
return mMethodVisitor.visitParameterAnnotation(arg0, arg1, arg2);
}
public void visitTableSwitchInsn(int arg0,
int arg1,
Label arg2,
Label[] arg3) {
mMethodVisitor.visitTableSwitchInsn(arg0, arg1, arg2, arg3);
clear();
}
public void visitTryCatchBlock(Label arg0,
Label arg1,
Label arg2,
String arg3) {
mMethodVisitor.visitTryCatchBlock(arg0, arg1, arg2, arg3);
clear();
}
public void visitTypeInsn(int opCode, String desc) {
mMethodVisitor.visitTypeInsn(opCode, desc);
}
public void visitVarInsn(int opCode, int index) {
mMethodVisitor.visitVarInsn(opCode, index);
switch (opCode) {
case Opcodes.ALOAD:
if (index == 0 && !mIsStatic) {
pushTextualDescription("this");
} else {
/*
* TODO Translate the index to a local variable name. To be able
* to do that we probably have to analyze the class in two steps
* since the visit method visitLocalVariable is not called until
* after all calls to visitVarInsn.
*/
pushTextualDescription("<localVariable" + index + ">");
}
break;
case Opcodes.ASTORE:
pop();
break;
default:
clear();
}
}
public AnnotationVisitor visitAnnotation(String arg0, boolean arg1) {
return mMethodVisitor.visitAnnotation(arg0, arg1);
}
public AnnotationVisitor visitAnnotationDefault() {
return mMethodVisitor.visitAnnotationDefault();
}
public void visitAttribute(Attribute arg0) {
mMethodVisitor.visitAttribute(arg0);
}
public void visitMaxs(int arg0, int arg1) {
mMethodVisitor.visitMaxs(arg0, arg1);
}
}