blob: 0c5a77e66337dd4b77904a309c4f742a07d873c0 [file] [log] [blame]
package test.javassist.bytecode.analysis;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.Bytecode;
import javassist.bytecode.CodeIterator;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
import javassist.bytecode.analysis.Analyzer;
import javassist.bytecode.analysis.Frame;
import javassist.bytecode.analysis.Type;
import junit.framework.TestCase;
/**
* Tests Analyzer
*
* @author Jason T. Greene
*/
public class AnalyzerTest extends TestCase {
public void testCommonSupperArray() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(getClass().getName() + "$Dummy");
CtMethod method = clazz.getDeclaredMethod("commonSuperArray");
verifyArrayLoad(clazz, method, "java.lang.Number");
}
public void testCommonInterfaceArray() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(getClass().getName() + "$Dummy");
CtMethod method = clazz.getDeclaredMethod("commonInterfaceArray");
verifyArrayLoad(clazz, method, "java.io.Serializable");
}
public void testSharedInterfaceAndSuperClass() throws Exception {
CtMethod method = ClassPool.getDefault().getMethod(
getClass().getName() + "$Dummy", "sharedInterfaceAndSuperClass");
verifyReturn(method, "java.io.Serializable");
method = ClassPool.getDefault().getMethod(
getClass().getName() + "$Dummy", "sharedOffsetInterfaceAndSuperClass");
verifyReturn(method, "java.io.Serializable");
method = ClassPool.getDefault().getMethod(
getClass().getName() + "$Dummy", "sharedSuperWithSharedInterface");
verifyReturn(method, getClass().getName() + "$Dummy$A");
}
public void testArrayDifferentDims() throws Exception {
CtMethod method = ClassPool.getDefault().getMethod(
getClass().getName() + "$Dummy", "arrayDifferentDimensions1");
verifyReturn(method, "java.lang.Cloneable[]");
method = ClassPool.getDefault().getMethod(
getClass().getName() + "$Dummy", "arrayDifferentDimensions2");
verifyReturn(method, "java.lang.Object[][]");
}
public void testReusedLocalMerge() throws Exception {
CtMethod method = ClassPool.getDefault().getMethod(
getClass().getName() + "$Dummy", "reusedLocalMerge");
MethodInfo info = method.getMethodInfo2();
Analyzer analyzer = new Analyzer();
Frame[] frames = analyzer.analyze(method.getDeclaringClass(), info);
assertNotNull(frames);
int pos = findOpcode(info, Opcode.RETURN);
Frame frame = frames[pos];
assertEquals("java.lang.Object", frame.getLocal(2).getCtClass().getName());
}
private static int findOpcode(MethodInfo info, int opcode) throws BadBytecode {
CodeIterator iter = info.getCodeAttribute().iterator();
// find return
int pos = 0;
while (iter.hasNext()) {
pos = iter.next();
if (iter.byteAt(pos) == opcode)
break;
}
return pos;
}
private static void verifyReturn(CtMethod method, String expected) throws BadBytecode {
MethodInfo info = method.getMethodInfo2();
CodeIterator iter = info.getCodeAttribute().iterator();
// find areturn
int pos = 0;
while (iter.hasNext()) {
pos = iter.next();
if (iter.byteAt(pos) == Opcode.ARETURN)
break;
}
Analyzer analyzer = new Analyzer();
Frame[] frames = analyzer.analyze(method.getDeclaringClass(), info);
assertNotNull(frames);
Frame frame = frames[pos];
assertEquals(expected, frame.peek().getCtClass().getName());
}
private static void verifyArrayLoad(CtClass clazz, CtMethod method, String component)
throws BadBytecode {
MethodInfo info = method.getMethodInfo2();
CodeIterator iter = info.getCodeAttribute().iterator();
// find aaload
int pos = 0;
while (iter.hasNext()) {
pos = iter.next();
if (iter.byteAt(pos) == Opcode.AALOAD)
break;
}
Analyzer analyzer = new Analyzer();
Frame[] frames = analyzer.analyze(clazz, info);
assertNotNull(frames);
Frame frame = frames[pos];
assertNotNull(frame);
Type type = frame.getStack(frame.getTopIndex() - 1);
assertEquals(component + "[]", type.getCtClass().getName());
pos = iter.next();
frame = frames[pos];
assertNotNull(frame);
type = frame.getStack(frame.getTopIndex());
assertEquals(component, type.getCtClass().getName());
}
private static void addJump(Bytecode code, int opcode, int pos) {
int current = code.currentPc();
code.addOpcode(opcode);
code.addIndex(pos - current);
}
public void testDeadCode() throws Exception {
CtMethod method = generateDeadCode(ClassPool.getDefault());
Analyzer analyzer = new Analyzer();
Frame[] frames = analyzer.analyze(method.getDeclaringClass(), method.getMethodInfo2());
assertNotNull(frames);
assertNull(frames[4]);
assertNotNull(frames[5]);
verifyReturn(method, "java.lang.String");
}
public void testInvalidCode() throws Exception {
CtMethod method = generateInvalidCode(ClassPool.getDefault());
Analyzer analyzer = new Analyzer();
try {
analyzer.analyze(method.getDeclaringClass(), method.getMethodInfo2());
} catch (BadBytecode e) {
return;
}
fail("Invalid code should have triggered a BadBytecode exception");
}
public void testCodeFalloff() throws Exception {
CtMethod method = generateCodeFalloff(ClassPool.getDefault());
Analyzer analyzer = new Analyzer();
try {
analyzer.analyze(method.getDeclaringClass(), method.getMethodInfo2());
} catch (BadBytecode e) {
return;
}
fail("Code falloff should have triggered a BadBytecode exception");
}
public void testJsrMerge() throws Exception {
CtMethod method = generateJsrMerge(ClassPool.getDefault());
Analyzer analyzer = new Analyzer();
analyzer.analyze(method.getDeclaringClass(), method.getMethodInfo2());
verifyReturn(method, "java.lang.String");
}
public void testJsrMerge2() throws Exception {
CtMethod method = generateJsrMerge2(ClassPool.getDefault());
Analyzer analyzer = new Analyzer();
analyzer.analyze(method.getDeclaringClass(), method.getMethodInfo2());
verifyReturn(method, "java.lang.String");
}
private CtMethod generateDeadCode(ClassPool pool) throws Exception {
CtClass clazz = pool.makeClass(getClass().getName() + "$Generated0");
CtClass stringClass = pool.get("java.lang.String");
CtMethod method = new CtMethod(stringClass, "foo", new CtClass[0], clazz);
MethodInfo info = method.getMethodInfo2();
info.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
Bytecode code = new Bytecode(info.getConstPool(), 1, 2);
/* 0 */ code.addIconst(1);
/* 1 */ addJump(code, Opcode.GOTO, 5);
/* 4 */ code.addIconst(0); // DEAD
/* 5 */ code.addIconst(1);
/* 6 */ code.addInvokestatic(stringClass, "valueOf", stringClass, new CtClass[]{CtClass.intType});
/* 9 */ code.addOpcode(Opcode.ARETURN);
info.setCodeAttribute(code.toCodeAttribute());
clazz.addMethod(method);
return method;
}
private CtMethod generateInvalidCode(ClassPool pool) throws Exception {
CtClass clazz = pool.makeClass(getClass().getName() + "$Generated4");
CtClass intClass = pool.get("java.lang.Integer");
CtClass stringClass = pool.get("java.lang.String");
CtMethod method = new CtMethod(stringClass, "foo", new CtClass[0], clazz);
MethodInfo info = method.getMethodInfo2();
info.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
Bytecode code = new Bytecode(info.getConstPool(), 1, 2);
/* 0 */ code.addIconst(1);
/* 1 */ code.addInvokestatic(intClass, "valueOf", intClass, new CtClass[]{CtClass.intType});
/* 4 */ code.addOpcode(Opcode.ARETURN);
info.setCodeAttribute(code.toCodeAttribute());
clazz.addMethod(method);
return method;
}
private CtMethod generateCodeFalloff(ClassPool pool) throws Exception {
CtClass clazz = pool.makeClass(getClass().getName() + "$Generated3");
CtClass stringClass = pool.get("java.lang.String");
CtMethod method = new CtMethod(stringClass, "foo", new CtClass[0], clazz);
MethodInfo info = method.getMethodInfo2();
info.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
Bytecode code = new Bytecode(info.getConstPool(), 1, 2);
/* 0 */ code.addIconst(1);
/* 1 */ code.addInvokestatic(stringClass, "valueOf", stringClass, new CtClass[]{CtClass.intType});
info.setCodeAttribute(code.toCodeAttribute());
clazz.addMethod(method);
return method;
}
private CtMethod generateJsrMerge(ClassPool pool) throws Exception {
CtClass clazz = pool.makeClass(getClass().getName() + "$Generated1");
CtClass stringClass = pool.get("java.lang.String");
CtMethod method = new CtMethod(stringClass, "foo", new CtClass[0], clazz);
MethodInfo info = method.getMethodInfo2();
info.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
Bytecode code = new Bytecode(info.getConstPool(), 1, 2);
/* 0 */ code.addIconst(5);
/* 1 */ code.addIstore(0);
/* 2 */ addJump(code, Opcode.JSR, 7);
/* 5 */ code.addAload(0);
/* 6 */ code.addOpcode(Opcode.ARETURN);
/* 7 */ code.addAstore(1);
/* 8 */ code.addIconst(3);
/* 9 */ code.addInvokestatic(stringClass, "valueOf", stringClass, new CtClass[]{CtClass.intType});
/* 12 */ code.addAstore(0);
/* 12 */ code.addRet(1);
info.setCodeAttribute(code.toCodeAttribute());
clazz.addMethod(method);
//System.out.println(clazz.toClass().getMethod("foo", new Class[0]).invoke(null, new Object[0]));
return method;
}
private CtMethod generateJsrMerge2(ClassPool pool) throws Exception {
CtClass clazz = pool.makeClass(getClass().getName() + "$Generated2");
CtClass stringClass = pool.get("java.lang.String");
CtMethod method = new CtMethod(stringClass, "foo", new CtClass[0], clazz);
MethodInfo info = method.getMethodInfo2();
info.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
Bytecode code = new Bytecode(info.getConstPool(), 1, 2);
/* 0 */ addJump(code, Opcode.JSR, 5);
/* 3 */ code.addAload(0);
/* 4 */ code.addOpcode(Opcode.ARETURN);
/* 5 */ code.addAstore(1);
/* 6 */ code.addIconst(4);
/* 7 */ code.addInvokestatic(stringClass, "valueOf", stringClass, new CtClass[]{CtClass.intType});
/* 10 */ code.addAstore(0);
/* 11 */ code.addRet(1);
info.setCodeAttribute(code.toCodeAttribute());
clazz.addMethod(method);
return method;
}
public static class Dummy {
public Serializable commonSuperArray(int x) {
Number[] n;
if (x > 5) {
n = new Long[10];
} else {
n = new Double[5];
}
return n[x];
}
public Serializable commonInterfaceArray(int x) {
Serializable[] n;
if (x > 5) {
n = new Long[10];
} else if (x > 3) {
n = new Double[5];
} else {
n = new String[3];
}
return n[x];
}
public static class A {};
public static class B1 extends A implements Serializable {};
public static class B2 extends A implements Serializable {};
public static class A2 implements Serializable, Cloneable {};
public static class A3 implements Serializable, Cloneable {};
public static class B3 extends A {};
public static class C31 extends B3 implements Serializable {};
public void dummy(Serializable s) {}
public Object sharedInterfaceAndSuperClass(int x) {
Serializable s;
if (x > 5) {
s = new B1();
} else {
s = new B2();
}
dummy(s);
return s;
}
public A sharedSuperWithSharedInterface(int x) {
A a;
if (x > 5) {
a = new B1();
} else if (x > 3) {
a = new B2();
} else {
a = new C31();
}
return a;
}
public void reusedLocalMerge() {
ArrayList list = new ArrayList();
try {
Iterator i = list.iterator();
i.hasNext();
} catch (Exception e) {
}
}
public Object sharedOffsetInterfaceAndSuperClass(int x) {
Serializable s;
if (x > 5) {
s = new B1();
} else {
s = new C31();
}
dummy(s);
return s;
}
public Object arrayDifferentDimensions1(int x) {
Object[] n;
if ( x > 5) {
n = new Number[1][1];
} else {
n = new Cloneable[1];
}
return n;
}
public Object arrayDifferentDimensions2(int x) {
Object[] n;
if ( x> 5) {
n = new String[1][1];
} else {
n = new Number[1][1][1][1];
}
return n;
}
}
}