]> source.dussan.org Git - javassist.git/commitdiff
fiexed JASSIST-238
authorchibash <chiba@javassist.org>
Sat, 22 Nov 2014 16:08:22 +0000 (01:08 +0900)
committerchibash <chiba@javassist.org>
Sat, 22 Nov 2014 16:08:22 +0000 (01:08 +0900)
12 files changed:
Readme.html
javassist.jar
src/main/javassist/bytecode/Bytecode.java
src/main/javassist/compiler/MemberCodeGen.java
src/main/javassist/compiler/MemberResolver.java
src/main/javassist/compiler/Parser.java
src/main/javassist/compiler/TypeChecker.java
src/test/Test.java
src/test/javassist/JvstTest.java
src/test/javassist/JvstTest4.java
src/test/javassist/JvstTest5.java [new file with mode: 0644]
src/test/test5/DefaultMethod.java [new file with mode: 0644]

index 9782bfe44a6c37b9e76bdeafcd146a8af0ba389b..73ab11cb908ce911613b5639fdfc98dcefa53dca 100644 (file)
@@ -283,7 +283,8 @@ see javassist.Dump.
 
 <p>-version 3.19
 <ul>
-<li>JIRA JASSIST-158, 205, 206, 207, 208, 209, 211, 212, 216, 220, 223, 224, 227, 230, 234, 235, 236.
+<li>JIRA JASSIST-158, 205, 206, 207, 208, 209, 211, 212, 216, 220, 223, 224,
+        227, 230, 234, 235, 236, 237.
 </ul>
 </p>
 
index 0f8bbecf4b14e73102c9a96f8731cc099ca8b18f..58b7db8b21ecc0aaef34f8449fc34a8b5d2089f8 100644 (file)
Binary files a/javassist.jar and b/javassist.jar differ
index fb4d3ec9fe3661dc98671af5f1e8fc9a8d615955..051619d1d8f154e8ae5086c07afa9297fb34c0d6 100644 (file)
@@ -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);
     }
 
index 167e4cccf4f490068d9e21e50b77bef92450cbfe..f799eea07e3dce00a306fa1019a27f951c2888d6 100644 (file)
@@ -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();
index f304b034b83643d73144385b237a800b627ab5bd..8f7591cf6309689efff68d80e774ba29f1cd2b7c 100644 (file)
@@ -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('.', '/');
     }
index 05b33184dd6294483a7ee30b7096beccd59c08d2..2a1d5d6369a2ebb4a7554ce8e03fd0de57079ef2 100644 (file)
@@ -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));
index 35a8e37882d80192f60c1e21aaf7ace129bea796..d28146268e3482dfaf04bd1ce5885b3dc331f775 100644 (file)
@@ -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();
@@ -694,6 +700,26 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
         throw new CompileError("bad method");
     }
 
+    /**
+     * 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.
index a6d31c680bf501ae08db54d34cd679166a863f0b..1aa7f8e2139449ec2e912a34faa20ec5f923d3df 100644 (file)
@@ -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); }
 }
index 596d62e20a7c9f253cf856d66193443346c3cbab..791b0820e0ca3ad4f04e363e76211475d9d5fb0d 100644 (file)
@@ -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);
index 3cc7d061da5b2a6fa4a772a5c5ac68cea9506520..d5616056b9ddd1f33524ba6518fbb02d86921d73 100644 (file)
@@ -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 (file)
index 0000000..32cebbe
--- /dev/null
@@ -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 (file)
index 0000000..7cabb1f
--- /dev/null
@@ -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);
+    }
+}