FULL PRODUCT VERSION : java version "1.8.0_112" Java(TM) SE Runtime Environment (build 1.8.0_112-b16) Java HotSpot(TM) 64-Bit Server VM (build 25.112-b16, mixed mode) FULL OS VERSION : Darwin <redacted> 16.1.0 Darwin Kernel Version 16.1.0: Thu Oct 13 21:26:57 PDT 2016; root:xnu-3789.21.3~60/RELEASE_X86_64 x86_64 EXTRA RELEVANT SYSTEM CONFIGURATION : macOS Sierra 10.12.1 A DESCRIPTION OF THE PROBLEM : The methods in the following code are instrumented to enclose the entire method body in a try-catch block. With this instrumentation, X(String) constructor passes class verification at runtime while X(X) constructor fails class verification with VerifyError -- Current frame's flags are not assignable to stack map frame's. Since the bodies of the constructor is identical, both constructors should produce identical results under identical instrumentation; however, this doesn't seem to be the case. public class X extends Y { X(String s) { // does not fail super(new Y(3)); } X(X s) { // fails super(new Y(3)); } /* public static void main(final String[] s) { new X("Hi"); } */ } class Y { Y(Object o) {} Y(int i) {} } THE PROBLEM WAS REPRODUCIBLE WITH -Xint FLAG: Yes THE PROBLEM WAS REPRODUCIBLE WITH -server FLAG: Yes STEPS TO FOLLOW TO REPRODUCE THE PROBLEM : Execute the following commands with ASM version 5.1 (http://forge.ow2.org/project/showfiles.php?group_id=23) and the provided source placed in XDump.java. javac -cp <path/to/asm-all-5.1.jar> XDump.java java -cp <path/to/asm-all-5.1.jar>:. XDump java -cp . X EXPECTED VERSUS ACTUAL BEHAVIOR : Instead of the class being successfully loaded, VerifyError is encountered while verifying X(X) method but not while verifying X(String) ERROR MESSAGES/STACK TRACES THAT OCCUR : Error: A JNI error has occurred, please check your installation and try again Exception in thread "main" java.lang.VerifyError: Stack map does not match the one at exception handler 13 Exception Details: Location: X.<init>(LX;)V @13: athrow Reason: Current frame's flags are not assignable to stack map frame's. Current Frame: bci: @0 flags: { flagThisUninit } locals: { uninitializedThis, 'X' } stack: { 'java/lang/Throwable' } Stackmap Frame: bci: @13 flags: { } locals: { top, 'X' } stack: { 'java/lang/Throwable' } Bytecode: 0x0000000: 2abb 0004 5906 b700 09b7 000c b1bf Exception Handler Table: bci [0, 13] => handler: 13 Stackmap Table: full_frame(@13,{Top,Object[#2]},{Object[#14]}) at java.lang.Class.getDeclaredMethods0(Native Method) at java.lang.Class.privateGetDeclaredMethods(Class.java:2701) at java.lang.Class.privateGetMethodRecursive(Class.java:3048) at java.lang.Class.getMethod0(Class.java:3018) at java.lang.Class.getMethod(Class.java:1784) at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544) at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526) REPRODUCIBILITY : This bug can be reproduced always. ---------- BEGIN SOURCE ---------- import java.io.*; import java.nio.file.Files; import java.util.*; import org.objectweb.asm.*; import static org.objectweb.asm.Opcodes.*; public class XDump { public static void main(String[] s) throws Exception { byte[] b = dump(); Files.write(new File("./X.class").toPath(), b); } public static byte[] dump () throws Exception { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); FieldVisitor fv; MethodVisitor mv; AnnotationVisitor av0; cw.visit(52, ACC_PUBLIC + ACC_SUPER, "X", null, "Y", null); { mv = cw.visitMethod(0, "<init>", "(Ljava/lang/String;)V", null, null); mv.visitCode(); Label begin = new Label(); mv.visitLabel(begin); mv.visitVarInsn(ALOAD, 0); mv.visitTypeInsn(NEW, "Y"); mv.visitInsn(DUP); mv.visitInsn(ICONST_3); mv.visitMethodInsn(INVOKESPECIAL, "Y", "<init>", "(I)V", false); mv.visitMethodInsn(INVOKESPECIAL, "Y", "<init>", "(Ljava/lang/Object;)V", false); mv.visitInsn(RETURN); Label end = new Label(); mv.visitLabel(end); mv.visitTryCatchBlock(begin, end, end, "java/lang/Throwable"); mv.visitInsn(Opcodes.ATHROW); mv.visitMaxs(4, 2); mv.visitEnd(); } { mv = cw.visitMethod(0, "<init>", "(LX;)V", null, null); mv.visitCode(); Label begin = new Label(); mv.visitLabel(begin); mv.visitVarInsn(ALOAD, 0); mv.visitTypeInsn(NEW, "Y"); mv.visitInsn(DUP); mv.visitInsn(ICONST_3); mv.visitMethodInsn(INVOKESPECIAL, "Y", "<init>", "(I)V", false); mv.visitMethodInsn(INVOKESPECIAL, "Y", "<init>", "(Ljava/lang/Object;)V", false); mv.visitInsn(RETURN); Label end = new Label(); mv.visitLabel(end); mv.visitTryCatchBlock(begin, end, end, "java/lang/Throwable"); mv.visitInsn(Opcodes.ATHROW); mv.visitMaxs(4, 2); mv.visitEnd(); } cw.visitEnd(); return cw.toByteArray(); } } ---------- END SOURCE ---------- CUSTOMER SUBMITTED WORKAROUND : It works if try-catch handler is split into two: the first one covers the code part before the call to super() and the second one covers the code part after the call to super(). This means the call to super() cannot be covered for exceptions.
|