|
|
|
Hi Arno,
as stated above under environment I tried with both JDK 1.4.2 and JDK 1.5.0. I also used Janino 2.5.6, 2.4.7, 2.4.3 and 2.3.16 with varying success, i.e. only Janino 2.3.16 yielded a class file I could execute without an exception being thrown. As it seems I messed the formatting up when copying the code into the description. The offending piece of code is LOG.getClass(). bye Hi Arno,
I did some research on that issue and this is what I found: Janino 2.5.6 seems to have a problem when generating bytecode for calling a method of an interface if this method is not declared in that interface Here is an example: package test;
import java.util.ArrayList;
import java.util.List;
public class Janinotest {
public static void main(String[] args) {
List list = new ArrayList();
System.out.println(list.getClass());
}
}
When you compile this class with janino 2.5.6 and try to execute the code you get the exception: Exception in thread "main" java.lang.IncompatibleClassChangeError at test.Janinotest.main(Janinotest.java:9) line 9 in the source code above is: System.out.println(list.getClass()); But when you compile this class with janino 2.3.16 or 2.3.17 or with Sun's javac 1.4.2 or 1.5.0 you get the correct behaviour and the program reports class java.util.ArrayList when executed. If you take a look at the generated bytecode utilising Sun's javap the problem is easy to see: 12: invokevirtual #27; //Method java/util/List.getClass:()Ljava/lang/Class; while janino 2.3.16, 2.3.17 and javac 1.4.2, 1.5.0 translate it to 12: invokevirtual #25; //Method java/lang/Object.getClass:()Ljava/lang/Class; I think this is where the problem originates in. I suppose the bytecode generated with janino 2.3.16, 2.3.17 or javac 1.4.2, 1.5.0 is correct while the bytecode generated with janino 2.5.6 is incorrect because the getClass() method should be called on java.lang.Object. As I discovered further, calling the getClass() method on java.util.List instead of on java.lang.Object occurs the first time (after janino 2.3.16) in janino 2.3.18. I had a look into the janino 2.5.6 sources and I think the problem might be the code at UnitCompiler.java line 2823: targetType = this.compileGetValue(this.toRvalueOrCE(mi.optionalTarget)); The call of compileGetValue returns an IClass object for targetType that represents a java.util.List instead of a java.lang.Object. targetType itself is used in line 2885: this.writeConstantMethodrefInfo(
targetType.getDescriptor(), // locatable
iMethod.getName(), // classFD
iMethod.getDescriptor() // methodMD
);
If you replace line 2885 with this.writeConstantMethodrefInfo(
iMethod.getDeclaringIClass().getDescriptor()
iMethod.getName(), // classFD
iMethod.getDescriptor() // methodMD
);
you get the following bytecode generated 12: invokevirtual #25; //Method java/lang/Object.getClass:()Ljava/lang/Class; and the exception doesn't occur. However I'm not a janino expert and so I guess this approach has other undesirable side effects. I think it may be better to change line 2823 or the method compileGetValue() that returns targetType. Maybe it would be best to set targetType = iMethod.getDeclaringIClass().getDescriptor(); under certain circumstances, for instance when iMethod.getDeclaringIClass().getDescriptor() represents java.lang.Object. In this context I found something very interesting: When you compile the following class package test;
import java.util.ArrayList;
import java.util.List;
public class Janinotest2 {
public static void main(String[] args) {
List list = new ArrayList();
System.out.println(list.hashCode());
}
}
with janino 2.3.16, 2.3.17 or janino 2.5.6 you get list.hashCode() compiled to 12: invokeinterface #27, 1; //InterfaceMethod java/util/List.hashCode:()I and I think this is what everybody would expect because the method hashCode() is explicitly declared in the interfaces java.util.List and java.util.Collection. The Sun compilers (javac 1.4.2 and 1.5.0) however translate list.hashCode() to 12: invokevirtual #5; //Method java/lang/Object.hashCode:()I The compiled programs work for all versions alike but it seems that javac codes a invokevirtual call for java.lang.Object whenever the declaring class is java.lang.Object. bye Hi Matthias,
your proposal to change line 2885 is correct. I did all kinds of regression tests, and everything works fine. Fix will be released as 2.5.7. |
|||||||||||||||||||||||||||||||||||||||||||||||||||
which JDK version and JANINO version do you use? Which line is line 10? I reckon the "public static final LOG..." line.
CU
Arno