Community
Participate
Working Groups
Eclipse Compiler 3.2 and 3.3 seems to generate wrong bytecode for nested try-finally blocks (see the code for reproducing below). Apache Harmony throws VerifyError when running such class (java.lang.VerifyError: (class: Test, method: test()Z) A subroutine splits execution into several ret instructions) and BCEL 5.2 tool also reports verification issue (Instruction ' 53: ret[169](2) 1' is part of more than one subroutine (or of the top level and a subroutine)). Please find more details below (see instruction '53: ret 1', it seems to return from 2 different subroutines depending on execution branch). It seems that Sun verifier doesn't check this specific constraint in this case, but Apache Harmony and BCEL tool strictly follow specification which says: "When executing the ret instruction, which implements a return from a subroutine, there must be only one possible subroutine from which the instruction can be returning." I might probably misunderstand what the specification says or how the bytecode below can be interpreted, so I would be very grateful for the explanations then. Thanks! To reproduce: 1. Create Test.java: ------------------------------------- public class Test { public boolean test() { try { Thread.sleep(10); } catch (Exception e) { } finally { try { try { Thread.sleep(10); } finally { Thread.sleep(10); } } catch (Exception e) { } } return true; } public static void main(String[] args) { new Test().test(); System.out.println("SUCCESS"); } } ------------------------------------- 2. Compile Test.java (below) with Eclipse 3.2/3.3 compiler: C:\jdk1.5.0_06\bin\java.exe -classpath c:\work\eut_eclipse\eclipse\plugins\org.eclipse.jdt.core_3.2.0.v_671.jar org.eclipse.jdt.internal.compiler.batch.Main Test.java 3. Output on Apache Harmony: =============================== Apache Harmony Launcher : (c) Copyright 1991, 2006 The Apache Software Foundation or its licensors, as applicable. java version "1.5.0" pre-alpha : not complete or compatible svn = r547521, (Jun 15 2007), Windows/ia32/msvc 1310, release build http://harmony.apache.org Uncaught exception in main: java.lang.VerifyError: (class: Test, method: test()Z) A subroutine splits execution into several ret instructions at java.lang.ClassLoader.defineClass0(ClassLoader.java) at java.lang.ClassLoader.defineClass(ClassLoader.java:438) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:70) at java.net.URLClassLoader.access$3(URLClassLoader.java:1) at java.net.URLClassLoader$URLHandler.createClass(URLClassLoader.java:261) at java.net.URLClassLoader$URLFileHandler.findClass(URLClassLoader.java:560) at java.net.URLClassLoader.findClassImpl(URLClassLoader.java:1194) at java.net.URLClassLoader$4.run(URLClassLoader.java:889) at java.net.URLClassLoader$4.run(URLClassLoader.java:1) at java.security.AccessController.doPrivilegedImpl(AccessController.java:171) at java.security.AccessController.doPrivileged(AccessController.java:64) at java.net.URLClassLoader.findClass(URLClassLoader.java:891) at java.lang.ClassLoader.loadClass(ClassLoader.java:575) at java.lang.ClassLoader$SystemClassLoader.loadClass(ClassLoader.java:963) at java.lang.ClassLoader.loadClass(ClassLoader.java:319) FAILED to invoke JVM. ================================ Note, that if '-noverify' option is specified the test passes ok and all "finally" blocks are executed correctly. Output on Sun Java 1.5 (with or without -Xfuture): java version "1.5.0_06" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_06-b05) Java HotSpot(TM) Client VM (build 1.5.0_06-b05, mixed mode) SUCCESS 4. The bytecode below corresponds to Eclipse compiler 3.2 compiled .class file: ----------------- Compiled from "Test.java" public class Test extends java.lang.Object{ public Test(); Code: 0: aload_0 1: invokespecial #8; //Method java/lang/Object."<init>":()V 4: return public boolean test(); Code: 0: ldc2_w #13; //long 10l 3: invokestatic #15; //Method java/lang/Thread.sleep:(J)V 6: goto 55 9: pop 10: goto 55 13: astore_2 14: jsr 19 17: aload_2 18: athrow 19: astore_1 20: ldc2_w #13; //long 10l 23: invokestatic #15; //Method java/lang/Thread.sleep:(J)V 26: goto 46 29: astore 4 31: jsr 37 34: aload 4 36: athrow 37: astore_3 38: ldc2_w #13; //long 10l 41: invokestatic #15; //Method java/lang/Thread.sleep:(J)V 44: ret 3 46: jsr 37 49: goto 53 52: pop 53: ret 1 55: jsr 19 58: iconst_1 59: ireturn Exception table: from to target type 0 9 9 Class java/lang/Exception 0 13 13 any 55 58 13 any 20 29 29 any 46 49 29 any 20 52 52 Class java/lang/Exception public static void main(java.lang.String[]); Code: 0: new #1; //class Test 3: dup 4: invokespecial #25; //Method "<init>":()V 7: invokevirtual #26; //Method test:()Z 10: pop 11: getstatic #28; //Field java/lang/System.out:Ljava/io/PrintStream; 14: ldc #34; //String SUCCESS 16: invokevirtual #36; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 19: return } 5. BCEL 5.2 tool (http://jakarta.apache.org/bcel) reports: $java -cp C:\work\harmony-jdk-r540603\jre\lib\boot\bcel-5.2\bcel-5.2.jar;. org.apache.bcel.verifier.Verifier Test.class .... Pass 3b, method number 1 ['public boolean test()']: VERIFIED_REJECTED Constraint violated in method 'public boolean test()': Instruction ' 53: ret[169](2) 1' is part of more than one subroutine (or of the top level and a subroutine). ....
Here is a trackback URL: http://issues.apache.org/jira/browse/HARMONY-3862
A workaround is to use the option to inline jsrs.
Same bug is found in old 1.4 javac version (1.4.0_04). Latest versions of javac 1.4.2 are inlining the inner finally blocks. So they never end up with a shared ret bytecodes.
Using the option -XDjsrlimit=0 with latest jdk1.4.2 produces a .class file that leads to the same error.
I think the problem comes from the exception handlers. In theory, the line [20 52 52 Class java/lang/Exception] means that it is possible to escape from nested finally back into outer one; which must cause grief to bytecode verifier. Need to check code generation, which is supposed to properly exit/reenter code blocks as appropriate (and this would indicate the bug if not doing it properly). I believe it should generate a sub interval for the nested finally block.
Actually, the exception handler is correct, to ensure the proper control transfer if an exception occurs at pc 41. Could it be that the spec is being too strict ? And the reason why Sun's verifier doesn't enforce the extra constraint ?
It seems that it is legal to return to a higher level in the subroutines call chain. From the JVMS (2nd edition): http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#9308 "Each instance of type returnAddress can be returned to at most once. If a ret instruction returns to a point in the subroutine call chain above the ret instruction corresponding to a given instance of type returnAddress, then that instance can never be used as a return address." This would mean that as long as the ret instruction is executed only once, this is fined. It would be a verify error if the ret 3 could be executed after the ret 1 has been executed. So I would close this one as WONTFIX since the code generation is actually fine and it seems that the Harmony bytecode verifier is too strict. Philippe, any comment on this ?
Makes sense, otherwise, I don't see how we could generate proper bytecode meeting runtime expectation (other than by inlining some subroutines, but this would feel a bit of a hack). I'd like to hear back from Harmony verifier...
Then we should close as INVALID. I am waiting from an update coming from the Harmony team.
Closing as INVALID.
Thanks, Philippe and Olivier, for an excellent citation. It seems the following is true: a subroutine may conditionally branch to the code which is part of an upper level subroutine.
Verified by reporter