diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main/javassist/bytecode/Bytecode.java | 37 | ||||
-rw-r--r-- | src/main/javassist/compiler/MemberCodeGen.java | 58 | ||||
-rw-r--r-- | src/main/javassist/compiler/MemberResolver.java | 14 | ||||
-rw-r--r-- | src/main/javassist/compiler/Parser.java | 6 | ||||
-rw-r--r-- | src/main/javassist/compiler/TypeChecker.java | 68 | ||||
-rw-r--r-- | src/test/Test.java | 25 | ||||
-rw-r--r-- | src/test/javassist/JvstTest.java | 1 | ||||
-rw-r--r-- | src/test/javassist/JvstTest4.java | 6 | ||||
-rw-r--r-- | src/test/javassist/JvstTest5.java | 34 | ||||
-rw-r--r-- | src/test/test5/DefaultMethod.java | 19 |
10 files changed, 194 insertions, 74 deletions
diff --git a/src/main/javassist/bytecode/Bytecode.java b/src/main/javassist/bytecode/Bytecode.java index fb4d3ec9..051619d1 100644 --- a/src/main/javassist/bytecode/Bytecode.java +++ b/src/main/javassist/bytecode/Bytecode.java @@ -924,11 +924,14 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode { * @see Descriptor#ofConstructor(CtClass[]) */ public void addInvokespecial(CtClass clazz, String name, String desc) { - addInvokespecial(constPool.addClassInfo(clazz), name, desc); + boolean isInterface = clazz == null ? false : clazz.isInterface(); + addInvokespecial(isInterface, + constPool.addClassInfo(clazz), name, desc); } /** - * Appends INVOKESPECIAL. + * Appends INVOKESPECIAL. The invoked method must not be a default + * method declared in an interface. * * @param clazz the fully-qualified class name. * @param name the method name @@ -938,11 +941,12 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode { * @see Descriptor#ofConstructor(CtClass[]) */ public void addInvokespecial(String clazz, String name, String desc) { - addInvokespecial(constPool.addClassInfo(clazz), name, desc); + addInvokespecial(false, constPool.addClassInfo(clazz), name, desc); } /** - * Appends INVOKESPECIAL. + * Appends INVOKESPECIAL. The invoked method must not be a default + * method declared in an interface. * * @param clazz the index of <code>CONSTANT_Class_info</code> * structure. @@ -953,8 +957,31 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode { * @see Descriptor#ofConstructor(CtClass[]) */ public void addInvokespecial(int clazz, String name, String desc) { + addInvokespecial(false, clazz, name, desc); + } + + /** + * Appends INVOKESPECIAL. + * + * @param isInterface true if the invoked method is a default method + * declared in an interface. + * @param clazz the index of <code>CONSTANT_Class_info</code> + * structure. + * @param name the method name + * @param desc the descriptor of the method signature. + * + * @see Descriptor#ofMethod(CtClass,CtClass[]) + * @see Descriptor#ofConstructor(CtClass[]) + */ + public void addInvokespecial(boolean isInterface, int clazz, String name, String desc) { add(INVOKESPECIAL); - addIndex(constPool.addMethodrefInfo(clazz, name, desc)); + int index; + if (isInterface) + index = constPool.addInterfaceMethodrefInfo(clazz, name, desc); + else + index = constPool.addMethodrefInfo(clazz, name, desc); + + addIndex(index); growStack(Descriptor.dataSize(desc) - 1); } diff --git a/src/main/javassist/compiler/MemberCodeGen.java b/src/main/javassist/compiler/MemberCodeGen.java index 167e4ccc..f799eea0 100644 --- a/src/main/javassist/compiler/MemberCodeGen.java +++ b/src/main/javassist/compiler/MemberCodeGen.java @@ -489,30 +489,44 @@ public class MemberCodeGen extends CodeGen { } else if (op == '.') { ASTree target = e.oprand1(); - if (target instanceof Keyword) - if (((Keyword)target).get() == SUPER) - isSpecial = true; - - try { - target.accept(this); + String classFollowedByDotSuper = TypeChecker.isDotSuper(target); + if (classFollowedByDotSuper != null) { + isSpecial = true; + targetClass = MemberResolver.getSuperInterface(thisClass, + classFollowedByDotSuper); + if (inStaticMethod || (cached != null && cached.isStatic())) + isStatic = true; // should be static + else { + aload0pos = bytecode.currentPc(); + bytecode.addAload(0); // this + } } - catch (NoFieldException nfe) { - if (nfe.getExpr() != target) - throw nfe; - - // it should be a static method. - exprType = CLASS; - arrayDim = 0; - className = nfe.getField(); // JVM-internal - isStatic = true; + else { + if (target instanceof Keyword) + if (((Keyword)target).get() == SUPER) + isSpecial = true; + + try { + target.accept(this); + } + catch (NoFieldException nfe) { + if (nfe.getExpr() != target) + throw nfe; + + // it should be a static method. + exprType = CLASS; + arrayDim = 0; + className = nfe.getField(); // JVM-internal + isStatic = true; + } + + if (arrayDim > 0) + targetClass = resolver.lookupClass(javaLangObject, true); + else if (exprType == CLASS /* && arrayDim == 0 */) + targetClass = resolver.lookupClassByJvmName(className); + else + badMethod(); } - - if (arrayDim > 0) - targetClass = resolver.lookupClass(javaLangObject, true); - else if (exprType == CLASS /* && arrayDim == 0 */) - targetClass = resolver.lookupClassByJvmName(className); - else - badMethod(); } else badMethod(); diff --git a/src/main/javassist/compiler/MemberResolver.java b/src/main/javassist/compiler/MemberResolver.java index f304b034..8f7591cf 100644 --- a/src/main/javassist/compiler/MemberResolver.java +++ b/src/main/javassist/compiler/MemberResolver.java @@ -21,6 +21,7 @@ import java.lang.ref.WeakReference; import java.util.WeakHashMap; import java.util.List; import java.util.Iterator; + import javassist.*; import javassist.bytecode.*; import javassist.compiler.ast.*; @@ -523,6 +524,19 @@ public class MemberResolver implements TokenId { + c.getName()); } + public static CtClass getSuperInterface(CtClass c, String interfaceName) + throws CompileError + { + try { + CtClass[] intfs = c.getInterfaces(); + for (int i = 0; i < intfs.length; i++) + if (intfs[i].getName().equals(interfaceName)) + return intfs[i]; + } catch (NotFoundException e) {} + throw new CompileError("cannot find the super inetrface " + interfaceName + + " of " + c.getName()); + } + public static String javaToJvmName(String classname) { return classname.replace('.', '/'); } diff --git a/src/main/javassist/compiler/Parser.java b/src/main/javassist/compiler/Parser.java index 05b33184..2a1d5d63 100644 --- a/src/main/javassist/compiler/Parser.java +++ b/src/main/javassist/compiler/Parser.java @@ -1001,6 +1001,7 @@ public final class Parser implements TokenId { * | postfix.expr "." Identifier * | postfix.expr ( "[" "]" )* "." CLASS * | postfix.expr "#" Identifier + * | postfix.expr "." SUPER * * "#" is not an operator of regular Java. It separates * a class name and a member name in an expression for static member @@ -1058,9 +1059,10 @@ public final class Parser implements TokenId { case '.' : lex.get(); t = lex.get(); - if (t == CLASS) { + if (t == CLASS) expr = parseDotClass(expr, 0); - } + else if (t == SUPER) + expr = Expr.make('.', new Symbol(toClassName(expr)), new Keyword(t)); else if (t == Identifier) { str = lex.getString(); expr = Expr.make('.', expr, new Member(str)); diff --git a/src/main/javassist/compiler/TypeChecker.java b/src/main/javassist/compiler/TypeChecker.java index 35a8e378..d2814626 100644 --- a/src/main/javassist/compiler/TypeChecker.java +++ b/src/main/javassist/compiler/TypeChecker.java @@ -656,28 +656,34 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { false); else if (op == '.') { ASTree target = e.oprand1(); - try { - target.accept(this); - } - catch (NoFieldException nfe) { - if (nfe.getExpr() != target) - throw nfe; - - // it should be a static method. - exprType = CLASS; - arrayDim = 0; - className = nfe.getField(); // JVM-internal - e.setOperator(MEMBER); - e.setOprand1(new Symbol(MemberResolver.jvmToJavaName( - className))); + String classFollowedByDotSuper = isDotSuper(target); + if (classFollowedByDotSuper != null) + targetClass = MemberResolver.getSuperInterface(thisClass, + classFollowedByDotSuper); + else { + try { + target.accept(this); + } + catch (NoFieldException nfe) { + if (nfe.getExpr() != target) + throw nfe; + + // it should be a static method. + exprType = CLASS; + arrayDim = 0; + className = nfe.getField(); // JVM-internal + e.setOperator(MEMBER); + e.setOprand1(new Symbol(MemberResolver.jvmToJavaName( + className))); + } + + if (arrayDim > 0) + targetClass = resolver.lookupClass(javaLangObject, true); + else if (exprType == CLASS /* && arrayDim == 0 */) + targetClass = resolver.lookupClassByJvmName(className); + else + badMethod(); } - - if (arrayDim > 0) - targetClass = resolver.lookupClass(javaLangObject, true); - else if (exprType == CLASS /* && arrayDim == 0 */) - targetClass = resolver.lookupClassByJvmName(className); - else - badMethod(); } else badMethod(); @@ -695,6 +701,26 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { } /** + * Returns non-null if target is something like Foo.super + * for accessing the default method in an interface. + * Otherwise, null. + * + * @return the class name followed by {@code .super} or null. + */ + static String isDotSuper(ASTree target) { + if (target instanceof Expr) { + Expr e = (Expr)target; + if (e.getOperator() == '.') { + ASTree right = e.oprand2(); + if (right instanceof Keyword && ((Keyword)right).get() == SUPER) + return ((Symbol)e.oprand1()).get(); + } + } + + return null; + } + + /** * @return a pair of the class declaring the invoked method * and the MethodInfo of that method. Never null. */ diff --git a/src/test/Test.java b/src/test/Test.java index a6d31c68..1aa7f8e2 100644 --- a/src/test/Test.java +++ b/src/test/Test.java @@ -2,27 +2,10 @@ import javassist.*; public class Test { public static void main(String[] args) throws Exception { - if (args.length > 1) { - new Test().bar(3); - return; - } - ClassPool cp = ClassPool.getDefault(); - CtClass inner3 = cp.get("test2.Anon$Anon2.1"); - CtBehavior ct = inner3.getEnclosingBehavior(); -/* CtClass str = cp.get("java.lang.String"); - CtClass cc = cp.get("Test"); - cc.getClassFile().setMajorVersion(javassist.bytecode.ClassFile.JAVA_4); - CtMethod m = cc.getDeclaredMethod("bar"); - m.addLocalVariable("aVar", str); - m.insertAfter(" dismiss( aVar );" , true); - cc.getClassFile().setMajorVersion(javassist.bytecode.ClassFile.JAVA_7); - m.insertBefore("aVar = initVar();"); - cc.writeFile();*/ + CtClass cc = cp.get("test5.DefaultMethod"); + CtMethod m = CtNewMethod.make("public int run(){ return test5.DefaultMethodIntf.super.foo(); }", cc); + cc.addMethod(m); + cc.writeFile(); } - - public void bar(int i) { foo(i); } - public void foo(int i) { System.out.println(i); } - public String initVar() { return "init"; } - public void dismiss(String s) { System.out.println(s); } } diff --git a/src/test/javassist/JvstTest.java b/src/test/javassist/JvstTest.java index 596d62e2..791b0820 100644 --- a/src/test/javassist/JvstTest.java +++ b/src/test/javassist/JvstTest.java @@ -1108,6 +1108,7 @@ public class JvstTest extends JvstTestRoot { suite.addTestSuite(JvstTest2.class); suite.addTestSuite(JvstTest3.class); suite.addTestSuite(JvstTest4.class); + suite.addTestSuite(JvstTest5.class); suite.addTestSuite(LoaderTestByRandall.class); suite.addTestSuite(javassist.bytecode.BytecodeTest.class); suite.addTestSuite(javassist.bytecode.StackMapTest.class); diff --git a/src/test/javassist/JvstTest4.java b/src/test/javassist/JvstTest4.java index 3cc7d061..d5616056 100644 --- a/src/test/javassist/JvstTest4.java +++ b/src/test/javassist/JvstTest4.java @@ -5,7 +5,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.HashSet; -import java.util.List; import javassist.bytecode.*; import javassist.bytecode.annotation.Annotation; @@ -662,8 +661,9 @@ public class JvstTest4 extends JvstTestRoot { long t2 = endTime2 - endTime; long t3 = endTime3 - endTime2; System.out.println("JIRA150: " + t1 + ", " + t2 + ", " + t3); - assertTrue(t2 < t1 * 5); - assertTrue(t3 < t1 * 3); + assertTrue("performance test (the next try may succeed): " + t1 + "/ 5 < " + t2, + t2 < t1 * 5); + assertTrue("", t3 < t1 * 3); } public void testJIRA150b() throws Exception { diff --git a/src/test/javassist/JvstTest5.java b/src/test/javassist/JvstTest5.java new file mode 100644 index 00000000..32cebbe2 --- /dev/null +++ b/src/test/javassist/JvstTest5.java @@ -0,0 +1,34 @@ +package javassist; + +public class JvstTest5 extends JvstTestRoot { + public JvstTest5(String name) { + super(name); + } + + public void testDollarClassInStaticMethod() throws Exception { + CtClass cc = sloader.makeClass("test5.DollarClass"); + CtMethod m = CtNewMethod.make("public static int run(){ return $class.getName().length(); }", cc); + cc.addMethod(m); + m = CtNewMethod.make("public int run2(){ return $class.getName().length(); }", cc); + cc.addMethod(m); + cc.writeFile(); + Object obj = make(cc.getName()); + assertEquals(cc.getName().length(), invoke(obj, "run")); + assertEquals(cc.getName().length(), invoke(obj, "run2")); + } + + public void testSuperDefaultMethodCall() throws Exception { + CtClass cc = sloader.get("test5.DefaultMethod"); + CtMethod m = CtNewMethod.make("public int run(){ return test5.DefaultMethodIntf.super.foo(); }", cc); + cc.addMethod(m); + m = CtNewMethod.make("public int run2(){ return test5.DefaultMethodIntf.baz(); }", cc); + cc.addMethod(m); + m = CtNewMethod.make("public int run3(){ return test5.DefaultMethodIntf.super.baz(); }", cc); + cc.addMethod(m); + cc.writeFile(); + Object obj = make(cc.getName()); + assertEquals(1, invoke(obj, "run")); + assertEquals(10, invoke(obj, "run2")); + assertEquals(10, invoke(obj, "run3")); + } +} diff --git a/src/test/test5/DefaultMethod.java b/src/test/test5/DefaultMethod.java new file mode 100644 index 00000000..7cabb1f3 --- /dev/null +++ b/src/test/test5/DefaultMethod.java @@ -0,0 +1,19 @@ +package test5; + +interface DefaultMethodSupIntf { + default int foo() { return 0; } +} + +interface DefaultMethodIntf extends DefaultMethodSupIntf { + default int foo() { return 1; } + static int baz() { return 10; } +} + +public class DefaultMethod implements DefaultMethodIntf { + public int bar() { return DefaultMethodIntf.super.foo(); } + + public static void main(String[] args) { + int i = new DefaultMethod().bar() + new DefaultMethod().foo() + DefaultMethodIntf.baz(); + System.out.println(i); + } +} |