[jboss-cvs] javassist/src/test/test/javassist/bytecode/analysis ...
Jason Thomas Greene
jgreene at jboss.com
Sat May 24 01:13:15 EDT 2008
User: jgreene
Date: 08/05/24 01:13:15
Added: src/test/test/javassist/bytecode/analysis
AnalyzerTest.java ErrorFinder.java ScannerTest.java
Log:
Fix subtypeOf in CtArray
Introduce full data-flow analysis API
Fix AALOAD by using data-flow analysis to determine the type
Introduce a testsuite to the project
Add a framedump toolp
Revision Changes Path
1.1 date: 2008/05/24 05:13:15; author: jgreene; state: Exp;javassist/src/test/test/javassist/bytecode/analysis/AnalyzerTest.java
Index: AnalyzerTest.java
===================================================================
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;
}
}
}
1.1 date: 2008/05/24 05:13:15; author: jgreene; state: Exp;javassist/src/test/test/javassist/bytecode/analysis/ErrorFinder.java
Index: ErrorFinder.java
===================================================================
package test.javassist.bytecode.analysis;
import java.io.BufferedReader;
import java.io.FileReader;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.bytecode.analysis.Analyzer;
/**
* Simple testing tool that verifies class files can be analyzed.
*
* @author Jason T. Greene
*/
public class ErrorFinder {
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
String className = args[0];
if (!className.equals("-file")) {
analyzeClass(pool, className);
return;
}
FileReader reader = new FileReader(args[1]);
BufferedReader lineReader = new BufferedReader(reader);
String line = lineReader.readLine();
while (line != null) {
analyzeClass(pool, line);
line = lineReader.readLine();
}
}
private static void analyzeClass(ClassPool pool, String className) {
try {
CtClass clazz = pool.get(className);
CtMethod[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++)
analyzeMethod(clazz, methods[i]);
} catch (Throwable e) {
System.out.println("FAIL: CLASS: " + className + " " + e.getClass() + ":" + e.getMessage());
}
}
private static void analyzeMethod(CtClass clazz, CtMethod method) {
String methodName = clazz.getName() + "." + method.getName() + method.getSignature();
System.out.println("START: " + methodName);
Analyzer analyzer = new Analyzer();
try {
analyzer.analyze(clazz, method.getMethodInfo2());
System.out.println("SUCCESS: " + methodName);
} catch (Exception e) {
System.out.println("FAIL: " + methodName + " - " + (e.getMessage() == null ? e.getClass().getName() : e.getMessage()));
}
}
}
1.1 date: 2008/05/24 05:13:15; author: jgreene; state: Exp;javassist/src/test/test/javassist/bytecode/analysis/ScannerTest.java
Index: ScannerTest.java
===================================================================
package test.javassist.bytecode.analysis;
import java.io.IOException;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.Bytecode;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
import javassist.bytecode.analysis.Subroutine;
import javassist.bytecode.analysis.SubroutineScanner;
import junit.framework.TestCase;
/**
* Tests Subroutine Scanner
*
* @author Jason T. Greene
*/
public class ScannerTest extends TestCase {
public void testNestedFinally() throws Exception {
ClassPool pool = ClassPool.getDefault();
generate(pool);
CtClass clazz = pool.get("test.ScannerTest$GeneratedTest");
CtMethod method = clazz.getDeclaredMethod("doit");
SubroutineScanner scanner = new SubroutineScanner();
Subroutine[] subs = scanner.scan(method.getMethodInfo2());
verifySubroutine(subs, 31, 31, new int[]{125, 25});
verifySubroutine(subs, 32, 31, new int[]{125, 25});
verifySubroutine(subs, 33, 31, new int[]{125, 25});
verifySubroutine(subs, 60, 31, new int[]{125, 25});
verifySubroutine(subs, 61, 31, new int[]{125, 25});
verifySubroutine(subs, 63, 31, new int[]{125, 25});
verifySubroutine(subs, 66, 31, new int[]{125, 25});
verifySubroutine(subs, 69, 31, new int[]{125, 25});
verifySubroutine(subs, 71, 31, new int[]{125, 25});
verifySubroutine(subs, 74, 31, new int[]{125, 25});
verifySubroutine(subs, 76, 31, new int[]{125, 25});
verifySubroutine(subs, 77, 77, new int[]{111, 71});
verifySubroutine(subs, 79, 77, new int[]{111, 71});
verifySubroutine(subs, 80, 77, new int[]{111, 71});
verifySubroutine(subs, 82, 77, new int[]{111, 71});
verifySubroutine(subs, 85, 77, new int[]{111, 71});
verifySubroutine(subs, 88, 77, new int[]{111, 71});
verifySubroutine(subs, 90, 77, new int[]{111, 71});
verifySubroutine(subs, 93, 77, new int[]{111, 71});
verifySubroutine(subs, 95, 77, new int[]{111, 71});
verifySubroutine(subs, 96, 96, new int[]{106, 90});
verifySubroutine(subs, 98, 96, new int[]{106, 90});
verifySubroutine(subs, 99, 96, new int[]{106, 90});
verifySubroutine(subs, 101, 96, new int[]{106, 90});
verifySubroutine(subs, 104, 96, new int[]{106, 90});
verifySubroutine(subs, 106, 77, new int[]{111, 71});
verifySubroutine(subs, 109, 77, new int[]{111, 71});
verifySubroutine(subs, 111, 31, new int[]{125, 25});
verifySubroutine(subs, 114, 31, new int[]{125, 25});
verifySubroutine(subs, 117, 31, new int[]{125, 25});
verifySubroutine(subs, 118, 31, new int[]{125, 25});
verifySubroutine(subs, 120, 31, new int[]{125, 25});
verifySubroutine(subs, 123, 31, new int[]{125, 25});
}
private static void verifySubroutine(Subroutine[] subs, int pos, int start,
int[] callers) {
Subroutine sub = subs[pos];
assertNotNull(sub);
assertEquals(sub.start(), start);
for (int i = 0; i < callers.length; i++)
assertTrue(sub.callers().contains(Integer.valueOf(callers[i])));
}
private static void generate(ClassPool pool) throws CannotCompileException, IOException, NotFoundException {
// Generated from eclipse JDK4 compiler:
// public void doit(int x) {
// println("null");
// try {
// println("try");
// } catch (RuntimeException e) {
// e.printStackTrace();
// } finally {
// switch (x) {
// default:
// case 15:
// try {
// println("inner-try");
// } finally {
// try {
// println("inner-inner-try");
// } finally {
// println("inner-finally");
// }
// }
// break;
// case 1789:
// println("switch -17");
// }
// }
//}
CtClass clazz = pool.makeClass("test.ScannerTest$GeneratedTest");
CtMethod method = new CtMethod(CtClass.voidType, "doit", new CtClass[] {CtClass.intType}, clazz);
MethodInfo info = method.getMethodInfo2();
info.setAccessFlags(AccessFlag.PUBLIC);
CtClass stringClass = pool.get("java.lang.String");
Bytecode code = new Bytecode(info.getConstPool(), 2, 9);
/* 0 */ code.addAload(0);
/* 1 */ code.addLdc("start");
/* 3 */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass});
/* 6 */ code.addAload(0);
/* 7 */ code.addLdc("try");
/* 9 */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass});
/* 12 */ addJump(code, Opcode.GOTO, 125);
/* 14 */ code.addAstore(2);
/* 16 */ code.addAload(2);
/* 17 */ code.addInvokevirtual("java.lang.Exception", "printStackTrace", "()V");
/* 20 */ addJump(code, Opcode.GOTO, 125);
/* 23 */ code.addAstore(4);
/* 25 */ addJump(code, Opcode.JSR, 31);
/* 28 */ code.addAload(4);
/* 30 */ code.addOpcode(Opcode.ATHROW);
/* 31 */ code.addAstore(3);
/* 32 */ code.addIload(1);
int spos = code.currentPc();
/* 33 */ code.addOpcode(Opcode.LOOKUPSWITCH);
code.addIndex(0); // 2 bytes pad - gets us to 36
code.add32bit(60 - spos); // default
code.add32bit(2); // 2 pairs
code.add32bit(15); code.add32bit(60 - spos);
code.add32bit(1789); code.add32bit(117 - spos);
/* 60 */ code.addAload(0);
/* 61 */ code.addLdc("inner-try");
/* 63 */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass});
/* 66 */ addJump(code, Opcode.GOTO, 111);
/* 69 */ code.addAstore(6);
/* 71 */ addJump(code, Opcode.JSR, 77);
/* 74 */ code.addAload(6);
/* 76 */ code.add(Opcode.ATHROW);
/* 77 */ code.addAstore(5);
/* 79 */ code.addAload(0);
/* 80 */ code.addLdc("inner-inner-try");
/* 82 */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass});
/* 85 */ addJump(code, Opcode.GOTO, 106);
/* 88 */ code.addAstore(8);
/* 90 */ addJump(code, Opcode.JSR, 96);
/* 93 */ code.addAload(8);
/* 95 */ code.add(Opcode.ATHROW);
/* 96 */ code.addAstore(7);
/* 98 */ code.addAload(0);
/* 99 */ code.addLdc("inner-finally");
/* 101 */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass});
/* 104 */ code.addRet(7);
/* 106 */ addJump(code, Opcode.JSR, 96);
/* 109 */ code.addRet(5);
/* 111 */ addJump(code, Opcode.JSR, 77);
/* 114 */ addJump(code, Opcode.GOTO, 123);
/* 117 */ code.addAload(0);
/* 118 */ code.addLdc("switch - 1789");
/* 120 */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass});
/* 123 */ code.addRet(3);
/* 125 */ addJump(code, Opcode.JSR, 31);
/* 128 */ code.addOpcode(Opcode.RETURN);
code.addExceptionHandler(6, 12, 15, "java.lang.RuntimeException");
code.addExceptionHandler(6, 20, 23, 0);
code.addExceptionHandler(125, 128, 23, 0);
code.addExceptionHandler(60, 69, 69, 0);
code.addExceptionHandler(111, 114, 69, 0);
code.addExceptionHandler(79, 88, 88, 0);
code.addExceptionHandler(106, 109, 88, 0);
info.setCodeAttribute(code.toCodeAttribute());
clazz.addMethod(method);
clazz.writeFile("/tmp");
}
private static void addJump(Bytecode code, int opcode, int pos) {
int current = code.currentPc();
code.addOpcode(opcode);
code.addIndex(pos - current);
}
}
More information about the jboss-cvs-commits
mailing list