<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>
* @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
* @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.
* @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);
}
}
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();
import java.util.WeakHashMap;
import java.util.List;
import java.util.Iterator;
+
import javassist.*;
import javassist.bytecode.*;
import javassist.compiler.ast.*;
+ 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('.', '/');
}
* | 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
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));
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();
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.
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); }
}
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);
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.HashSet;
-import java.util.List;
import javassist.bytecode.*;
import javassist.bytecode.annotation.Annotation;
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 {
--- /dev/null
+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"));
+ }
+}
--- /dev/null
+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);
+ }
+}