diff options
author | jgreene <jgreene@30ef5769-5b8d-40dd-aea6-55b5d6557bb3> | 2008-05-24 05:13:20 +0000 |
---|---|---|
committer | jgreene <jgreene@30ef5769-5b8d-40dd-aea6-55b5d6557bb3> | 2008-05-24 05:13:20 +0000 |
commit | 2bae8b13291b8ef8fc3df24af627424f344581df (patch) | |
tree | 7d68840873257ab78ae69cc9ed9570adc18bc2d8 /src/test | |
parent | e48604525140c746404a637282cb7b1dd97c18c4 (diff) | |
download | javassist-2bae8b13291b8ef8fc3df24af627424f344581df.tar.gz javassist-2bae8b13291b8ef8fc3df24af627424f344581df.zip |
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
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@437 30ef5769-5b8d-40dd-aea6-55b5d6557bb3
Diffstat (limited to 'src/test')
-rw-r--r-- | src/test/test/Test.java | 10 | ||||
-rw-r--r-- | src/test/test/javassist/bytecode/analysis/AnalyzerTest.java | 411 | ||||
-rw-r--r-- | src/test/test/javassist/bytecode/analysis/ErrorFinder.java | 62 | ||||
-rw-r--r-- | src/test/test/javassist/bytecode/analysis/ScannerTest.java | 185 | ||||
-rw-r--r-- | src/test/test/javassist/convert/ArrayAccessReplaceTest.java | 407 |
5 files changed, 1065 insertions, 10 deletions
diff --git a/src/test/test/Test.java b/src/test/test/Test.java deleted file mode 100644 index 6d34165c..00000000 --- a/src/test/test/Test.java +++ /dev/null @@ -1,10 +0,0 @@ -package test; - -import javassist.*; - -public class Test { - public static void main(String[] args) throws Exception { - CtClass ctClass = ClassPool.getDefault().get("JavassistTarget"); - ctClass.getMethod("method", "(Ljava/lang/String;)V").insertAfter(""); - } -} diff --git a/src/test/test/javassist/bytecode/analysis/AnalyzerTest.java b/src/test/test/javassist/bytecode/analysis/AnalyzerTest.java new file mode 100644 index 00000000..0c5a77e6 --- /dev/null +++ b/src/test/test/javassist/bytecode/analysis/AnalyzerTest.java @@ -0,0 +1,411 @@ +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; + } + } +} diff --git a/src/test/test/javassist/bytecode/analysis/ErrorFinder.java b/src/test/test/javassist/bytecode/analysis/ErrorFinder.java new file mode 100644 index 00000000..a2128325 --- /dev/null +++ b/src/test/test/javassist/bytecode/analysis/ErrorFinder.java @@ -0,0 +1,62 @@ +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())); + } + } +} diff --git a/src/test/test/javassist/bytecode/analysis/ScannerTest.java b/src/test/test/javassist/bytecode/analysis/ScannerTest.java new file mode 100644 index 00000000..56114baf --- /dev/null +++ b/src/test/test/javassist/bytecode/analysis/ScannerTest.java @@ -0,0 +1,185 @@ +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); + } +} diff --git a/src/test/test/javassist/convert/ArrayAccessReplaceTest.java b/src/test/test/javassist/convert/ArrayAccessReplaceTest.java new file mode 100644 index 00000000..4c40849c --- /dev/null +++ b/src/test/test/javassist/convert/ArrayAccessReplaceTest.java @@ -0,0 +1,407 @@ +package test.javassist.convert; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.Map; + +import javassist.ClassPool; +import javassist.CodeConverter; +import javassist.CtClass; +import junit.framework.TestCase; + +public class ArrayAccessReplaceTest extends TestCase { + private static SimpleInterface simple; + + public void setUp() throws Exception { + ClassPool pool = new ClassPool(true); + CtClass echoClass = pool.get(ArrayAccessReplaceTest.class.getName() + "$Echo"); + CtClass simpleClass = pool.get(ArrayAccessReplaceTest.class.getName() + "$Simple"); + CodeConverter converter = new CodeConverter(); + converter.replaceArrayAccess(echoClass, new CodeConverter.DefaultArrayAccessReplacementMethodNames()); + simpleClass.instrument(converter); + simple = (SimpleInterface) simpleClass.toClass(new URLClassLoader(new URL[0], getClass().getClassLoader()), Class.class.getProtectionDomain()).newInstance(); + } + + public void testComplex() throws Exception { + ClassPool pool = new ClassPool(true); + CtClass clazz = pool.get(ArrayAccessReplaceTest.class.getName() + "$Complex"); + + CodeConverter converter = new CodeConverter(); + converter.replaceArrayAccess(clazz, new CodeConverter.DefaultArrayAccessReplacementMethodNames()); + clazz.instrument(converter); + clazz.writeFile("/tmp"); + ComplexInterface instance = (ComplexInterface) clazz.toClass(new URLClassLoader(new URL[0], getClass().getClassLoader()), Class.class.getProtectionDomain()).newInstance(); + assertEquals(Integer.valueOf(5), instance.complexRead(4)); + } + + public void testBoolean() throws Exception { + for (int i = 0; i < 100; i++) { + boolean value = i % 5 == 0; + simple.setBoolean(i, value); + } + + for (int i = 0; i < 100; i++) { + boolean value = i % 5 == 0; + assertEquals(value, simple.getBoolean(i)); + } + } + + public void testByte() throws Exception { + for (byte i = 0; i < 100; i++) { + simple.setByte(i, i); + } + + for (byte i = 0; i < 100; i++) { + assertEquals(i, simple.getByte(i)); + } + } + + public void testShort() throws Exception { + for (short i = 0; i < 100; i++) { + simple.setShort(i, i); + } + + for (short i = 0; i < 100; i++) { + assertEquals(i, simple.getShort(i)); + } + } + + public void testChar() throws Exception { + for (char i = 0; i < 100; i++) { + simple.setChar(i, i); + } + + for (char i = 0; i < 100; i++) { + assertEquals(i, simple.getChar(i)); + } + } + + public void testInt() throws Exception { + for (int i = 0; i < 100; i++) { + simple.setInt(i, i); + } + + for (int i = 0; i < 100; i++) { + assertEquals(i, simple.getInt(i)); + } + } + + public void testLong() throws Exception { + for (int i = 0; i < 100; i++) { + simple.setLong(i, i); + } + + for (int i = 0; i < 100; i++) { + assertEquals(i, simple.getLong(i)); + } + } + + public void testFloat() throws Exception { + for (int i = 0; i < 100; i++) { + simple.setFloat(i, i); + } + + for (int i = 0; i < 100; i++) { + assertEquals((float)i, simple.getFloat(i), 0); + } + } + + public void testDouble() throws Exception { + for (int i = 0; i < 100; i++) { + simple.setDouble(i, i); + } + + for (int i = 0; i < 100; i++) { + assertEquals((double)i, simple.getDouble(i), 0); + } + } + + public void testObject() throws Exception { + for (int i = 0; i < 100; i++) { + simple.setObject(i, Integer.valueOf(i)); + } + + for (int i = 0; i < 100; i++) { + assertEquals(Integer.valueOf(i), simple.getObject(i)); + } + } + + public void testFoo() throws Exception { + for (int i = 0; i < 100; i++) { + simple.setFoo(i, new Foo(i)); + } + + for (int i = 0; i < 100; i++) { + assertEquals(new Foo(i), simple.getFoo(i)); + } + } + + public static class Echo { + public static Map byteMap = new HashMap(); + public static Map charMap = new HashMap(); + public static Map doubleMap = new HashMap(); + public static Map floatMap = new HashMap(); + public static Map intMap = new HashMap(); + public static Map longMap = new HashMap(); + public static Map objectMap = new HashMap(); + public static Map shortMap = new HashMap(); + + public static Object arrayReadObject(Object array, int index) { + return objectMap.get(Integer.valueOf(index)); + } + + public static void arrayWriteObject(Object array, int index, Object element) { + objectMap.put(Integer.valueOf(index), element); + } + + public static byte arrayReadByteOrBoolean(Object array, int index) { + return ((Byte)byteMap.get(Integer.valueOf(index))).byteValue(); + } + + public static void arrayWriteByteOrBoolean(Object array, int index, byte element) { + byteMap.put(Integer.valueOf(index), Byte.valueOf(element)); + } + + public static char arrayReadChar(Object array, int index) { + return ((Character)charMap.get(Integer.valueOf(index))).charValue(); + } + + public static void arrayWriteChar(Object array, int index, char element) { + charMap.put(Integer.valueOf(index), Character.valueOf(element)); + } + + public static double arrayReadDouble(Object array, int index) { + return ((Double)doubleMap.get(Integer.valueOf(index))).doubleValue(); + } + + public static void arrayWriteDouble(Object array, int index, double element) { + doubleMap.put(Integer.valueOf(index), Double.valueOf(element)); + } + + public static float arrayReadFloat(Object array, int index) { + return ((Float)floatMap.get(Integer.valueOf(index))).floatValue(); + } + + public static void arrayWriteFloat(Object array, int index, float element) { + floatMap.put(Integer.valueOf(index), Float.valueOf(element)); + } + + public static int arrayReadInt(Object array, int index) { + return ((Integer)intMap.get(Integer.valueOf(index))).intValue(); + } + + public static void arrayWriteInt(Object array, int index, int element) { + intMap.put(Integer.valueOf(index), Integer.valueOf(element)); + } + + public static long arrayReadLong(Object array, int index) { + return ((Long)longMap.get(Integer.valueOf(index))).longValue(); + } + + public static void arrayWriteLong(Object array, int index, long element) { + longMap.put(Integer.valueOf(index), Long.valueOf(element)); + } + + public static short arrayReadShort(Object array, int index) { + return ((Short)shortMap.get(Integer.valueOf(index))).shortValue(); + } + + public static void arrayWriteShort(Object array, int index, short element) { + shortMap.put(Integer.valueOf(index), Short.valueOf(element)); + } + } + + public static class Foo { + public int bar; + + public Foo(int bar) { + this.bar = bar; + } + + public int hashCode() { + return bar; + } + + public boolean equals(Object o) { + if (! (o instanceof Foo)) + return false; + + return ((Foo)o).bar == bar; + } + } + + public static interface SimpleInterface { + public void setBoolean(int pos, boolean value); + public boolean getBoolean(int pos); + + public void setByte(int pos, byte value); + public byte getByte(int pos); + + public void setShort(int pos, short value); + public short getShort(int pos); + + public void setChar(int pos, char value); + public char getChar(int pos); + + public void setInt(int pos, int value); + public int getInt(int pos); + + public void setLong(int pos, long value); + public long getLong(int pos); + + public void setFloat(int pos, float value); + public float getFloat(int pos); + + public void setDouble(int pos, double value); + public double getDouble(int pos); + + public void setObject(int pos, Object value); + public Object getObject(int pos); + + public void setFoo(int pos, Foo value); + public Foo getFoo(int pos); + } + + public static class Simple implements SimpleInterface { + private boolean[] booleans; + private byte[] bytes; + private short[] shorts; + private char[] chars; + private int[] ints; + private long[] longs; + private float[] floats; + private double[] doubles; + private Object[] objects; + private Foo[] foos; + + public boolean getBoolean(int pos) { + return booleans[pos]; + } + + public byte getByte(int pos) { + return bytes[pos]; + } + + public char getChar(int pos) { + return chars[pos]; + } + + public double getDouble(int pos) { + return doubles[pos]; + } + + public float getFloat(int pos) { + return floats[pos]; + } + + public Foo getFoo(int pos) { + return foos[pos]; + } + + public int getInt(int pos) { + return ints[pos]; + } + + public long getLong(int pos) { + return longs[pos]; + } + + public Object getObject(int pos) { + return objects[pos]; + } + + public short getShort(int pos) { + return shorts[pos]; + } + + public void setBoolean(int pos, boolean value) { + booleans[pos] = value; + } + + public void setByte(int pos, byte value) { + bytes[pos] = value; + } + + public void setChar(int pos, char value) { + chars[pos] = value; + } + + public void setDouble(int pos, double value) { + doubles[pos] = value; + } + + public void setFloat(int pos, float value) { + floats[pos] = value; + } + + public void setFoo(int pos, Foo value) { + foos[pos] = value; + } + + public void setInt(int pos, int value) { + ints[pos] = value; + } + + public void setLong(int pos, long value) { + longs[pos] = value; + } + + public void setObject(int pos, Object value) { + objects[pos] = value; + } + + public void setShort(int pos, short value) { + shorts[pos] = value; + } + + } + + public static interface ComplexInterface { + public Number complexRead(int x); + } + + public static class Complex implements ComplexInterface { + private Integer[] nums; + private Long[] longNums; + private static Integer justRead; + + public static Object arrayReadObject(Object array, int offset) { + return Integer.valueOf(justRead.intValue() + offset); + } + + public static void arrayWriteObject(Object array, int offset, Object element) { + justRead = (Integer) element; + } + + public Object getInteger(int i) { + return (Object) Integer.valueOf(i); + } + + public Number complexRead(int x) { + Number[] ns = null; + Number n1, n2, n3, n4; + try { + ((Object[])ns)[1] = getInteger(x); + // We have to throw an error since we can't intercept + // a guaranteed null array read yet (likely never will be able to) + throw new Error("hi"); + } catch (Error error) { + ns = nums; + } catch (Exception exception) { + ns = longNums; + } finally { + n1 = ns[1]; + n2 = ns[2]; + n3 = ns[3]; + n4 = ns[4]; + + n2.intValue(); + n3.intValue(); + n4.intValue(); + } + + return n1; + } + } +}
\ No newline at end of file |