aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/javassist/bytecode/Bytecode.java37
-rw-r--r--src/main/javassist/compiler/MemberCodeGen.java58
-rw-r--r--src/main/javassist/compiler/MemberResolver.java14
-rw-r--r--src/main/javassist/compiler/Parser.java6
-rw-r--r--src/main/javassist/compiler/TypeChecker.java68
-rw-r--r--src/test/Test.java25
-rw-r--r--src/test/javassist/JvstTest.java1
-rw-r--r--src/test/javassist/JvstTest4.java6
-rw-r--r--src/test/javassist/JvstTest5.java34
-rw-r--r--src/test/test5/DefaultMethod.java19
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);
+ }
+}