<li>JIRA JASSIST-244, 245, 248, 250, 255, 256, 259, 262. | <li>JIRA JASSIST-244, 245, 248, 250, 255, 256, 259, 262. | ||||
<li><code>javassist.tools.Callback</code> was modified to be Java 1.4 compatible. | <li><code>javassist.tools.Callback</code> was modified to be Java 1.4 compatible. | ||||
The parameter type of <code>Callback#result()</code> was changed. | The parameter type of <code>Callback#result()</code> was changed. | ||||
<li>The algorithm for generating a stack-map table was modified to fix github issue #83. | |||||
<li>The algorithm for generating a stack-map table was modified to fix github issue #83. | |||||
<li>A bug of ProxyFactory related to default methods was fixed. It is github issue #45. | |||||
</ul> | </ul> | ||||
</p> | </p> | ||||
return minfo; | return minfo; | ||||
} | } | ||||
private static MethodInfo makeDelegator(Method meth, String desc, | |||||
private MethodInfo makeDelegator(Method meth, String desc, | |||||
ConstPool cp, Class declClass, String delegatorName) { | ConstPool cp, Class declClass, String delegatorName) { | ||||
MethodInfo delegator = new MethodInfo(cp, delegatorName, desc); | MethodInfo delegator = new MethodInfo(cp, delegatorName, desc); | ||||
delegator.setAccessFlags(Modifier.FINAL | Modifier.PUBLIC | delegator.setAccessFlags(Modifier.FINAL | Modifier.PUBLIC | ||||
Bytecode code = new Bytecode(cp, 0, 0); | Bytecode code = new Bytecode(cp, 0, 0); | ||||
code.addAload(0); | code.addAload(0); | ||||
int s = addLoadParameters(code, meth.getParameterTypes(), 1); | int s = addLoadParameters(code, meth.getParameterTypes(), 1); | ||||
code.addInvokespecial(declClass.isInterface(), cp.addClassInfo(declClass.getName()), | |||||
Class targetClass = invokespecialTarget(declClass); | |||||
code.addInvokespecial(targetClass.isInterface(), cp.addClassInfo(targetClass.getName()), | |||||
meth.getName(), desc); | meth.getName(), desc); | ||||
addReturn(code, meth.getReturnType()); | addReturn(code, meth.getReturnType()); | ||||
code.setMaxLocals(++s); | code.setMaxLocals(++s); | ||||
return delegator; | return delegator; | ||||
} | } | ||||
/* Suppose that the receiver type is S, the invoked method | |||||
* is declared in T, and U is the immediate super class of S | |||||
* (or its interface). If S <: U <: T (S <: T reads "S extends T"), | |||||
* the target type of invokespecial has to be not T but U. | |||||
*/ | |||||
private Class invokespecialTarget(Class declClass) { | |||||
if (declClass.isInterface()) | |||||
for (Class i: interfaces) | |||||
if (declClass.isAssignableFrom(i)) | |||||
return i; | |||||
return superClass; | |||||
} | |||||
/** | /** | ||||
* @param delegatorName null if the original method is abstract. | * @param delegatorName null if the original method is abstract. | ||||
*/ | */ |
public String extended() { return "ext"; } | public String extended() { return "ext"; } | ||||
public String base2() { return super.base2() + "ext2"; } | public String base2() { return super.base2() + "ext2"; } | ||||
} | } | ||||
String valueDefaultMethods = ""; | |||||
public void testDefaultMethods() throws Exception { | |||||
valueDefaultMethods = ""; | |||||
ProxyFactory f = new ProxyFactory(); | |||||
f.writeDirectory = "./proxy"; | |||||
f.setSuperclass(Default3.class); | |||||
Class c = f.createClass(); | |||||
MethodHandler mi = new MethodHandler() { | |||||
public Object invoke(Object self, Method m, Method proceed, | |||||
Object[] args) throws Throwable { | |||||
valueDefaultMethods += "1"; | |||||
return proceed.invoke(self, args); // execute the original method. | |||||
} | |||||
}; | |||||
Default3 foo = (Default3)c.newInstance(); | |||||
((Proxy)foo).setHandler(mi); | |||||
foo.foo(); | |||||
foo.bar(); | |||||
assertEquals("11", valueDefaultMethods); | |||||
} | |||||
public void testDefaultMethods2() throws Exception { | |||||
valueDefaultMethods = ""; | |||||
ProxyFactory f = new ProxyFactory(); | |||||
f.writeDirectory = "./proxy"; | |||||
f.setInterfaces(new Class[] { Default2.class }); | |||||
Class c = f.createClass(); | |||||
MethodHandler mi = new MethodHandler() { | |||||
public Object invoke(Object self, Method m, Method proceed, | |||||
Object[] args) throws Throwable { | |||||
valueDefaultMethods += "1"; | |||||
return proceed.invoke(self, args); // execute the original method. | |||||
} | |||||
}; | |||||
Default2 foo = (Default2)c.newInstance(); | |||||
((Proxy)foo).setHandler(mi); | |||||
foo.foo(); | |||||
foo.bar(); | |||||
assertEquals("11", valueDefaultMethods); | |||||
} | |||||
public static interface Default1 { | |||||
default int foo() { return 0; } | |||||
default int baz() { return 2; } | |||||
} | |||||
public static interface Default2 extends Default1 { | |||||
default int bar() { return 1; } | |||||
} | |||||
public static class Default3 implements Default2 { | |||||
public int foo() { return Default2.super.foo(); } | |||||
} | |||||
public static class Default4 extends Default3 { | |||||
public int baz() { return super.baz(); } | |||||
} | |||||
} | } |