Fri, 27 Sep 2013 13:36:25 -0400
8025260: Methodhandles/JSR292: NullPointerException (NPE) thrown instead of AbstractMethodError (AME)
Summary: Copied null-checks from templateInterpreter_CPU into methodHandles_CPU
Reviewed-by: jrose, twisti
1.1 --- a/src/cpu/sparc/vm/methodHandles_sparc.cpp Mon Sep 30 15:42:39 2013 -0700 1.2 +++ b/src/cpu/sparc/vm/methodHandles_sparc.cpp Fri Sep 27 13:36:25 2013 -0400 1.3 @@ -1,5 +1,5 @@ 1.4 /* 1.5 - * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. 1.6 + * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. 1.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.8 * 1.9 * This code is free software; you can redistribute it and/or modify it 1.10 @@ -121,6 +121,7 @@ 1.11 1.12 void MethodHandles::jump_from_method_handle(MacroAssembler* _masm, Register method, Register target, Register temp, 1.13 bool for_compiler_entry) { 1.14 + Label L_no_such_method; 1.15 assert(method == G5_method, "interpreter calling convention"); 1.16 assert_different_registers(method, target, temp); 1.17 1.18 @@ -133,6 +134,9 @@ 1.19 const Address interp_only(G2_thread, JavaThread::interp_only_mode_offset()); 1.20 __ ld(interp_only, temp); 1.21 __ cmp_and_br_short(temp, 0, Assembler::zero, Assembler::pt, run_compiled_code); 1.22 + // Null method test is replicated below in compiled case, 1.23 + // it might be able to address across the verify_thread() 1.24 + __ br_null_short(G5_method, Assembler::pn, L_no_such_method); 1.25 __ ld_ptr(G5_method, in_bytes(Method::interpreter_entry_offset()), target); 1.26 __ jmp(target, 0); 1.27 __ delayed()->nop(); 1.28 @@ -141,11 +145,19 @@ 1.29 // it doesn't matter, since this is interpreter code. 1.30 } 1.31 1.32 + // Compiled case, either static or fall-through from runtime conditional 1.33 + __ br_null_short(G5_method, Assembler::pn, L_no_such_method); 1.34 + 1.35 const ByteSize entry_offset = for_compiler_entry ? Method::from_compiled_offset() : 1.36 Method::from_interpreted_offset(); 1.37 __ ld_ptr(G5_method, in_bytes(entry_offset), target); 1.38 __ jmp(target, 0); 1.39 __ delayed()->nop(); 1.40 + 1.41 + __ bind(L_no_such_method); 1.42 + AddressLiteral ame(StubRoutines::throw_AbstractMethodError_entry()); 1.43 + __ jump_to(ame, temp); 1.44 + __ delayed()->nop(); 1.45 } 1.46 1.47 void MethodHandles::jump_to_lambda_form(MacroAssembler* _masm,
2.1 --- a/src/cpu/x86/vm/methodHandles_x86.cpp Mon Sep 30 15:42:39 2013 -0700 2.2 +++ b/src/cpu/x86/vm/methodHandles_x86.cpp Fri Sep 27 13:36:25 2013 -0400 2.3 @@ -1,5 +1,5 @@ 2.4 /* 2.5 - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. 2.6 + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 2.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.8 * 2.9 * This code is free software; you can redistribute it and/or modify it 2.10 @@ -114,6 +114,11 @@ 2.11 void MethodHandles::jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp, 2.12 bool for_compiler_entry) { 2.13 assert(method == rbx, "interpreter calling convention"); 2.14 + 2.15 + Label L_no_such_method; 2.16 + __ testptr(rbx, rbx); 2.17 + __ jcc(Assembler::zero, L_no_such_method); 2.18 + 2.19 __ verify_method_ptr(method); 2.20 2.21 if (!for_compiler_entry && JvmtiExport::can_post_interpreter_events()) { 2.22 @@ -138,6 +143,9 @@ 2.23 const ByteSize entry_offset = for_compiler_entry ? Method::from_compiled_offset() : 2.24 Method::from_interpreted_offset(); 2.25 __ jmp(Address(method, entry_offset)); 2.26 + 2.27 + __ bind(L_no_such_method); 2.28 + __ jump(RuntimeAddress(StubRoutines::throw_AbstractMethodError_entry())); 2.29 } 2.30 2.31 void MethodHandles::jump_to_lambda_form(MacroAssembler* _masm,
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/test/compiler/jsr292/methodHandleExceptions/ByteClassLoader.java Fri Sep 27 13:36:25 2013 -0400 3.3 @@ -0,0 +1,44 @@ 3.4 +/* 3.5 + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.7 + * 3.8 + * This code is free software; you can redistribute it and/or modify it 3.9 + * under the terms of the GNU General Public License version 2 only, as 3.10 + * published by the Free Software Foundation. 3.11 + * 3.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 3.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 3.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 3.15 + * version 2 for more details (a copy is included in the LICENSE file that 3.16 + * accompanied this code). 3.17 + * 3.18 + * You should have received a copy of the GNU General Public License version 3.19 + * 2 along with this work; if not, write to the Free Software Foundation, 3.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 3.21 + * 3.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 3.23 + * or visit www.oracle.com if you need additional information or have any 3.24 + * questions. 3.25 + * 3.26 + */ 3.27 + 3.28 +/** 3.29 + * A minimal classloader for loading bytecodes that could not result from 3.30 + * properly compiled Java. 3.31 + * 3.32 + * @author dr2chase 3.33 + */ 3.34 +public class ByteClassLoader extends ClassLoader { 3.35 + /** 3.36 + * (pre)load class name using classData for the definition. 3.37 + * 3.38 + * @param name 3.39 + * @param classData 3.40 + * @return 3.41 + */ 3.42 + public Class<?> loadBytes(String name, byte[] classData) { 3.43 + Class<?> clazz = defineClass(name, classData, 0, classData.length); 3.44 + resolveClass(clazz); 3.45 + return clazz; 3.46 + } 3.47 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/test/compiler/jsr292/methodHandleExceptions/C.java Fri Sep 27 13:36:25 2013 -0400 4.3 @@ -0,0 +1,33 @@ 4.4 +/* 4.5 + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.7 + * 4.8 + * This code is free software; you can redistribute it and/or modify it 4.9 + * under the terms of the GNU General Public License version 2 only, as 4.10 + * published by the Free Software Foundation. 4.11 + * 4.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 4.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 4.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 4.15 + * version 2 for more details (a copy is included in the LICENSE file that 4.16 + * accompanied this code). 4.17 + * 4.18 + * You should have received a copy of the GNU General Public License version 4.19 + * 2 along with this work; if not, write to the Free Software Foundation, 4.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 4.21 + * 4.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 4.23 + * or visit www.oracle.com if you need additional information or have any 4.24 + * questions. 4.25 + * 4.26 + */ 4.27 + 4.28 +/** 4.29 + * Test class -- implements I, which provides default for m, but this class 4.30 + * declares it abstract which (should) hide the interface default, and throw 4.31 + * an abstract method error if it is called (calling it requires bytecode hacking 4.32 + * or inconsistent compilation). 4.33 + */ 4.34 +public abstract class C implements I { 4.35 + public abstract int m(); 4.36 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/test/compiler/jsr292/methodHandleExceptions/I.java Fri Sep 27 13:36:25 2013 -0400 5.3 @@ -0,0 +1,27 @@ 5.4 +/* 5.5 + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5.7 + * 5.8 + * This code is free software; you can redistribute it and/or modify it 5.9 + * under the terms of the GNU General Public License version 2 only, as 5.10 + * published by the Free Software Foundation. 5.11 + * 5.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 5.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 5.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 5.15 + * version 2 for more details (a copy is included in the LICENSE file that 5.16 + * accompanied this code). 5.17 + * 5.18 + * You should have received a copy of the GNU General Public License version 5.19 + * 2 along with this work; if not, write to the Free Software Foundation, 5.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 5.21 + * 5.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 5.23 + * or visit www.oracle.com if you need additional information or have any 5.24 + * questions. 5.25 + * 5.26 + */ 5.27 + 5.28 +public interface I { 5.29 + default public int m() { return 1; } 5.30 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/test/compiler/jsr292/methodHandleExceptions/TestAMEnotNPE.java Fri Sep 27 13:36:25 2013 -0400 6.3 @@ -0,0 +1,143 @@ 6.4 +/* 6.5 + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 6.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6.7 + * 6.8 + * This code is free software; you can redistribute it and/or modify it 6.9 + * under the terms of the GNU General Public License version 2 only, as 6.10 + * published by the Free Software Foundation. 6.11 + * 6.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 6.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 6.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 6.15 + * version 2 for more details (a copy is included in the LICENSE file that 6.16 + * accompanied this code). 6.17 + * 6.18 + * You should have received a copy of the GNU General Public License version 6.19 + * 2 along with this work; if not, write to the Free Software Foundation, 6.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 6.21 + * 6.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 6.23 + * or visit www.oracle.com if you need additional information or have any 6.24 + * questions. 6.25 + * 6.26 + */ 6.27 + 6.28 +import java.lang.reflect.InvocationTargetException; 6.29 +import jdk.internal.org.objectweb.asm.ClassWriter; 6.30 +import jdk.internal.org.objectweb.asm.Handle; 6.31 +import jdk.internal.org.objectweb.asm.MethodVisitor; 6.32 +import jdk.internal.org.objectweb.asm.Opcodes; 6.33 + 6.34 +/** 6.35 + * @test 6.36 + * @bug 8025260 6.37 + * @summary Ensure that AbstractMethodError is thrown, not NullPointerException, through MethodHandles::jump_from_method_handle code path 6.38 + * 6.39 + * @compile -XDignore.symbol.file ByteClassLoader.java I.java C.java TestAMEnotNPE.java 6.40 + * @run main/othervm TestAMEnotNPE 6.41 + */ 6.42 + 6.43 +public class TestAMEnotNPE implements Opcodes { 6.44 + 6.45 + /** 6.46 + * The bytes for D, a NOT abstract class extending abstract class C 6.47 + * without supplying an implementation for abstract method m. 6.48 + * There is a default method in the interface I, but it should lose to 6.49 + * the abstract class. 6.50 + 6.51 + class D extends C { 6.52 + D() { super(); } 6.53 + // does not define m 6.54 + } 6.55 + 6.56 + * @return 6.57 + * @throws Exception 6.58 + */ 6.59 + public static byte[] bytesForD() throws Exception { 6.60 + 6.61 + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS); 6.62 + MethodVisitor mv; 6.63 + 6.64 + cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "D", null, "C", null); 6.65 + 6.66 + { 6.67 + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 6.68 + mv.visitCode(); 6.69 + mv.visitVarInsn(ALOAD, 0); 6.70 + mv.visitMethodInsn(INVOKESPECIAL, "C", "<init>", "()V"); 6.71 + mv.visitInsn(RETURN); 6.72 + mv.visitMaxs(0, 0); 6.73 + mv.visitEnd(); 6.74 + } 6.75 + cw.visitEnd(); 6.76 + 6.77 + return cw.toByteArray(); 6.78 + } 6.79 + 6.80 + 6.81 + /** 6.82 + * The bytecodes for an invokeExact of a particular methodHandle, I.m, invoked on a D 6.83 + 6.84 + class T { 6.85 + T() { super(); } // boring constructor 6.86 + int test() { 6.87 + MethodHandle mh = `I.m():int`; 6.88 + D d = new D(); 6.89 + return mh.invokeExact(d); // Should explode here, AbstractMethodError 6.90 + } 6.91 + } 6.92 + 6.93 + * @return 6.94 + * @throws Exception 6.95 + */ 6.96 + public static byte[] bytesForT() throws Exception { 6.97 + 6.98 + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS); 6.99 + MethodVisitor mv; 6.100 + 6.101 + cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "T", null, "java/lang/Object", null); 6.102 + { 6.103 + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 6.104 + mv.visitCode(); 6.105 + mv.visitVarInsn(ALOAD, 0); 6.106 + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); 6.107 + mv.visitInsn(RETURN); 6.108 + mv.visitMaxs(0,0); 6.109 + mv.visitEnd(); 6.110 + } 6.111 + { 6.112 + mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "test", "()I", null, null); 6.113 + mv.visitCode(); 6.114 + mv.visitLdcInsn(new Handle(Opcodes.H_INVOKEINTERFACE, "I", "m", "()I")); 6.115 + mv.visitTypeInsn(NEW, "D"); 6.116 + mv.visitInsn(DUP); 6.117 + mv.visitMethodInsn(INVOKESPECIAL, "D", "<init>", "()V"); 6.118 + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact", "(LI;)I"); 6.119 + mv.visitInsn(IRETURN); 6.120 + mv.visitMaxs(0,0); 6.121 + mv.visitEnd(); 6.122 + } 6.123 + cw.visitEnd(); 6.124 + return cw.toByteArray(); 6.125 + } 6.126 + 6.127 + public static void main(String args[] ) throws Throwable { 6.128 + ByteClassLoader bcl = new ByteClassLoader(); 6.129 + Class<?> d = bcl.loadBytes("D", bytesForD()); 6.130 + Class<?> t = bcl.loadBytes("T", bytesForT()); 6.131 + try { 6.132 + Object result = t.getMethod("test").invoke(null); 6.133 + System.out.println("Expected AbstractMethodError wrapped in InvocationTargetException, saw no exception"); 6.134 + throw new Error("Missing expected exception"); 6.135 + } catch (InvocationTargetException e) { 6.136 + Throwable th = e.getCause(); 6.137 + if (th instanceof AbstractMethodError) { 6.138 + th.printStackTrace(System.out); 6.139 + System.out.println("PASS, saw expected exception (AbstractMethodError, wrapped in InvocationTargetException)."); 6.140 + } else { 6.141 + System.out.println("Expected AbstractMethodError wrapped in InvocationTargetException, saw " + th); 6.142 + throw th; 6.143 + } 6.144 + } 6.145 + } 6.146 +}