diff --git a/src/share/vm/classfile/systemDictionary.hpp b/src/share/vm/classfile/systemDictionary.hpp index 1bf98676f6b6335b81f0854f68574f36ad4f36dc..e204d005fe67cd921813cb8a581ffc83c045f537 100644 --- a/src/share/vm/classfile/systemDictionary.hpp +++ b/src/share/vm/classfile/systemDictionary.hpp @@ -165,6 +165,7 @@ class SymbolPropertyTable; \ do_klass(StringBuffer_klass, java_lang_StringBuffer, Pre ) \ do_klass(StringBuilder_klass, java_lang_StringBuilder, Pre ) \ + do_klass(misc_Unsafe_klass, sun_misc_Unsafe, Pre ) \ \ /* It's NULL in non-1.4 JDKs. */ \ do_klass(StackTraceElement_klass, java_lang_StackTraceElement, Opt ) \ diff --git a/src/share/vm/classfile/vmSymbols.hpp b/src/share/vm/classfile/vmSymbols.hpp index 5d32b159efebb84103307a66557d4a30c704482f..9a06792793af15bc8f922157d19c118f7eb70fdf 100644 --- a/src/share/vm/classfile/vmSymbols.hpp +++ b/src/share/vm/classfile/vmSymbols.hpp @@ -331,6 +331,7 @@ template(findNative_name, "findNative") \ template(deadChild_name, "deadChild") \ template(addClass_name, "addClass") \ + template(throwIllegalAccessError_name, "throwIllegalAccessError") \ template(getFromClass_name, "getFromClass") \ template(dispatch_name, "dispatch") \ template(getSystemClassLoader_name, "getSystemClassLoader") \ diff --git a/src/share/vm/memory/universe.cpp b/src/share/vm/memory/universe.cpp index b393272e0640fba900743080c575a66e5c62c995..d022ae179133f2f71b6afb563aa518b357b249b5 100644 --- a/src/share/vm/memory/universe.cpp +++ b/src/share/vm/memory/universe.cpp @@ -120,6 +120,7 @@ oop Universe::_null_ptr_exception_instance = NULL; oop Universe::_arithmetic_exception_instance = NULL; oop Universe::_virtual_machine_error_instance = NULL; oop Universe::_vm_exception = NULL; +Method* Universe::_throw_illegal_access_error = NULL; Array* Universe::_the_empty_int_array = NULL; Array* Universe::_the_empty_short_array = NULL; Array* Universe::_the_empty_klass_array = NULL; @@ -1096,6 +1097,18 @@ bool universe_post_init() { Universe::_finalizer_register_cache->init( SystemDictionary::Finalizer_klass(), m); + InstanceKlass::cast(SystemDictionary::misc_Unsafe_klass())->link_class(CHECK_false); + m = InstanceKlass::cast(SystemDictionary::misc_Unsafe_klass())->find_method( + vmSymbols::throwIllegalAccessError_name(), + vmSymbols::void_method_signature()); + if (m != NULL && !m->is_static()) { + // Note null is okay; this method is used in itables, and if it is null, + // then AbstractMethodError is thrown instead. + tty->print_cr("Unable to link/verify Unsafe.throwIllegalAccessError method"); + return false; // initialization failed (cannot throw exception yet) + } + Universe::_throw_illegal_access_error = m; + // Setup method for registering loaded classes in class loader vector InstanceKlass::cast(SystemDictionary::ClassLoader_klass())->link_class(CHECK_false); m = InstanceKlass::cast(SystemDictionary::ClassLoader_klass())->find_method(vmSymbols::addClass_name(), vmSymbols::class_void_signature()); diff --git a/src/share/vm/memory/universe.hpp b/src/share/vm/memory/universe.hpp index a891d2a11ce582301039fcaf37c4807e6f67ac71..ec3b59f803ae57c2b3bb903d8606438b6588f52e 100644 --- a/src/share/vm/memory/universe.hpp +++ b/src/share/vm/memory/universe.hpp @@ -149,6 +149,8 @@ class Universe: AllStatic { static LatestMethodCache* _loader_addClass_cache; // method for registering loaded classes in class loader vector static LatestMethodCache* _pd_implies_cache; // method for checking protection domain attributes + static Method* _throw_illegal_access_error; + // preallocated error objects (no backtrace) static oop _out_of_memory_error_java_heap; static oop _out_of_memory_error_metaspace; @@ -305,6 +307,7 @@ class Universe: AllStatic { static oop arithmetic_exception_instance() { return _arithmetic_exception_instance; } static oop virtual_machine_error_instance() { return _virtual_machine_error_instance; } static oop vm_exception() { return _vm_exception; } + static Method* throw_illegal_access_error() { return _throw_illegal_access_error; } static Array* the_empty_int_array() { return _the_empty_int_array; } static Array* the_empty_short_array() { return _the_empty_short_array; } diff --git a/src/share/vm/oops/klassVtable.cpp b/src/share/vm/oops/klassVtable.cpp index 07a1c75682676fd75f91dbf5e1fb9ca8b28dca9e..1399a41baa8dc6956b89c546eb264277c8b2d0d1 100644 --- a/src/share/vm/oops/klassVtable.cpp +++ b/src/share/vm/oops/klassVtable.cpp @@ -1076,7 +1076,12 @@ void klassItable::initialize_itable_for_interface(int method_table_offset, Klass LinkResolver::lookup_instance_method_in_klasses(target, _klass, m->name(), m->signature(), CHECK); } if (target == NULL || !target->is_public() || target->is_abstract()) { - // Entry do not resolve. Leave it empty + // Entry does not resolve. Leave it empty for AbstractMethodError. + if (!(target == NULL) && !target->is_public()) { + // Stuff an IllegalAccessError throwing method in there instead. + itableOffsetEntry::method_entry(_klass(), method_table_offset)[m->itable_index()]. + initialize(Universe::throw_illegal_access_error()); + } } else { // Entry did resolve, check loader constraints before initializing // if checkconstraints requested diff --git a/test/compiler/jsr292/methodHandleExceptions/ByteClassLoader.java b/test/compiler/jsr292/methodHandleExceptions/ByteClassLoader.java index de257ad71ff97bb7cc72ddf59c1cceb854b5b3d6..3903f6f160c3f54d3e45996e943c6ed22485bf36 100644 --- a/test/compiler/jsr292/methodHandleExceptions/ByteClassLoader.java +++ b/test/compiler/jsr292/methodHandleExceptions/ByteClassLoader.java @@ -1,3 +1,12 @@ +import java.io.BufferedOutputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; + /* * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -23,12 +32,63 @@ */ /** - * A minimal classloader for loading bytecodes that could not result from - * properly compiled Java. + * A ByteClassLoader is used to define classes from collections of bytes, as + * well as loading classes in the usual way. It includes options to write the + * classes to files in a jar, or to read the classes from jars in a later or + * debugging run. + * + * If Boolean property byteclassloader.verbose is true, be chatty about jar + * file operations. * - * @author dr2chase */ -public class ByteClassLoader extends ClassLoader { +public class ByteClassLoader extends URLClassLoader { + + final static boolean verbose + = Boolean.getBoolean("byteclassloader.verbose"); + + final boolean read; + final JarOutputStream jos; + final String jar_name; + + /** + * Make a new ByteClassLoader. + * + * @param jar_name Basename of jar file to be read/written by this classloader. + * @param read If true, read classes from jar file instead of from parameter. + * @param write If true, write classes to jar files for offline study/use. + * + * @throws FileNotFoundException + * @throws IOException + */ + public ByteClassLoader(String jar_name, boolean read, boolean write) + throws FileNotFoundException, IOException { + super(read + ? new URL[]{new URL("file:" + jar_name + ".jar")} + : new URL[0]); + this.read = read; + this.jar_name = jar_name; + this.jos = write + ? new JarOutputStream( + new BufferedOutputStream( + new FileOutputStream(jar_name + ".jar"))) : null; + if (read && write) { + throw new Error("At most one of read and write may be true."); + } + } + + private static void writeJarredFile(JarOutputStream jos, String file, String suffix, byte[] bytes) { + String fileName = file.replace(".", "/") + "." + suffix; + JarEntry ze = new JarEntry(fileName); + try { + ze.setSize(bytes.length); + jos.putNextEntry(ze); + jos.write(bytes); + jos.closeEntry(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + /** * (pre)load class name using classData for the definition. * @@ -36,9 +96,36 @@ public class ByteClassLoader extends ClassLoader { * @param classData * @return */ - public Class loadBytes(String name, byte[] classData) { - Class clazz = defineClass(name, classData, 0, classData.length); - resolveClass(clazz); - return clazz; + public Class loadBytes(String name, byte[] classData) throws ClassNotFoundException { + if (jos != null) { + if (verbose) { + System.out.println("ByteClassLoader: writing " + name); + } + writeJarredFile(jos, name, "class", classData); + } + + Class clazz = null; + if (read) { + if (verbose) { + System.out.println("ByteClassLoader: reading " + name + " from " + jar_name); + } + clazz = loadClass(name); + } else { + clazz = defineClass(name, classData, 0, classData.length); + resolveClass(clazz); + } + return clazz; + } + + public void close() { + if (jos != null) { + try { + if (verbose) { + System.out.println("ByteClassLoader: closing " + jar_name); + } + jos.close(); + } catch (IOException ex) { + } + } } } diff --git a/test/compiler/jsr292/methodHandleExceptions/TestAMEnotNPE.java b/test/compiler/jsr292/methodHandleExceptions/TestAMEnotNPE.java index 2b41c15f87b4e119d1f0c3b84e8e4e58b042cf1f..0e525dec2f2b68ed0cdaa3c2518c0215ae71d04a 100644 --- a/test/compiler/jsr292/methodHandleExceptions/TestAMEnotNPE.java +++ b/test/compiler/jsr292/methodHandleExceptions/TestAMEnotNPE.java @@ -21,50 +21,127 @@ * questions. * */ - import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.Handle; import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.internal.org.objectweb.asm.Opcodes; +import p.Dok; /** - * @test - * @bug 8025260 - * @summary Ensure that AbstractMethodError is thrown, not NullPointerException, through MethodHandles::jump_from_method_handle code path + * @test @bug 8025260 8016839 + * @summary Ensure that AbstractMethodError and IllegalAccessError are thrown appropriately, not NullPointerException + * + * @compile -XDignore.symbol.file TestAMEnotNPE.java ByteClassLoader.java p/C.java p/Dok.java p/E.java p/F.java p/I.java p/Tdirect.java p/Treflect.java * - * @compile -XDignore.symbol.file ByteClassLoader.java I.java C.java TestAMEnotNPE.java * @run main/othervm TestAMEnotNPE + * @run main/othervm -Xint TestAMEnotNPE + * @run main/othervm -Xcomp TestAMEnotNPE */ - public class TestAMEnotNPE implements Opcodes { + static boolean writeJarFiles = false; + static boolean readJarFiles = false; + /** - * The bytes for D, a NOT abstract class extending abstract class C - * without supplying an implementation for abstract method m. - * There is a default method in the interface I, but it should lose to - * the abstract class. + * Optional command line parameter (any case-insensitive prefix of) + * "writejarfiles" or "readjarfiles". + * + * "Writejarfiles" creates a jar file for each different set of tested classes. + * "Readjarfiles" causes the classloader to use the copies of the classes + * found in the corresponding jar files. + * + * Jarfilenames look something like pD_ext_pF (p.D extends p.F) + * and qD_m_pp_imp_pI (q.D with package-private m implements p.I) + * + */ + public static void main(String args[]) throws Throwable { + ArrayList lt = new ArrayList(); + + if (args.length > 0) { + String a0 = args[0].toLowerCase(); + if (a0.length() > 0) { + writeJarFiles = ("writejarfiles").startsWith(a0); + readJarFiles = ("readjarfiles").startsWith(a0); + } + if (!(writeJarFiles || readJarFiles)) { + throw new Error("Command line parameter (if any) should be prefix of writeJarFiles or readJarFiles"); + } + } + + try { + System.out.println("TRYING p.D.m PRIVATE interface-invoked as p.I.m, p.D extends p.F, p.F.m FINAL"); + tryAndCheckThrown(lt, bytesForDprivateSubWhat("p/F"), + "p.D extends p.F (p.F implements p.I, FINAL public m), private m", + IllegalAccessError.class, "pD_ext_pF"); + // We'll take either a VerifyError (pre 2013-11-30) + // or an IllegalAccessError (post 2013-11-22) + } catch (VerifyError ve) { + System.out.println("Saw expected VerifyError " + ve); + } + System.out.println(); + + System.out.println("TRYING p.D.m PRIVATE interface-invoked as p.I.m, p.D extends p.E"); + tryAndCheckThrown(lt, bytesForDprivateSubWhat("p/E"), + "p.D extends p.E (p.E implements p.I, public m), private m", + IllegalAccessError.class, "pD_ext_pE"); + + System.out.println("TRYING p.D.m ABSTRACT interface-invoked as p.I.m"); + tryAndCheckThrown(lt, bytesForD(), + "D extends abstract C, no m", + AbstractMethodError.class, "pD_ext_pC"); - class D extends C { - D() { super(); } - // does not define m - } + System.out.println("TRYING q.D.m PACKAGE interface-invoked as p.I.m"); + tryAndCheckThrown(lt, "q.D", bytesForDsomeAccess("q/D", 0), + "q.D implements p.I, protected m", IllegalAccessError.class, + "qD_m_pp_imp_pI"); + // Note jar file name is used in the plural-arg case. + System.out.println("TRYING p.D.m PRIVATE interface-invoked as p.I.m"); + tryAndCheckThrown(lt, bytesForDsomeAccess("p/D", ACC_PRIVATE), + "p.D implements p.I, private m", + IllegalAccessError.class, "pD_m_pri_imp_pI"); + + // Plural-arg test. + System.out.println("TRYING p.D.m PRIVATE MANY ARG interface-invoked as p.I.m"); + tryAndCheckThrownMany(lt, bytesForDsomeAccess("p/D", ACC_PRIVATE), + "p.D implements p.I, private m", IllegalAccessError.class); + + if (lt.size() > 0) { + System.out.flush(); + Thread.sleep(250); // This de-interleaves output and error in Netbeans, sigh. + for (Throwable th : lt) + System.err.println(th); + throw new Error("Test failed, there were " + lt.size() + " failures listed above"); + } else { + System.out.println("ALL PASS, HOORAY!"); + } + } + + /** + * The bytes for D, a NOT abstract class extending abstract class C without + * supplying an implementation for abstract method m. There is a default + * method in the interface I, but it should lose to the abstract class. + * * @return * @throws Exception */ public static byte[] bytesForD() throws Exception { - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES + | ClassWriter.COMPUTE_MAXS); MethodVisitor mv; - cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "D", null, "C", null); + cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "p/D", null, "p/C", null); { mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, "C", "", "()V"); + mv.visitMethodInsn(INVOKESPECIAL, "p/C", "", "()V"); mv.visitInsn(RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); @@ -74,70 +151,346 @@ public class TestAMEnotNPE implements Opcodes { return cw.toByteArray(); } + /** + * The bytes for D, implements I, does not extend C, declares m()I with + * access method_acc. + * + * @param d_name Name of class defined + * @param method_acc Accessibility of that class's method m. + * @return + * @throws Exception + */ + public static byte[] bytesForDsomeAccess(String d_name, int method_acc) throws Exception { + return bytesForSomeDsubSomethingSomeAccess(d_name, "java/lang/Object", method_acc); + } + + /** + * The bytes for D implements I, extends some class, declares m()I as + * private. + * + * Invokeinterface of I.m applied to this D should throw IllegalAccessError + * + * @param sub_what The name of the class that D will extend. + * @return + * @throws Exception + */ + public static byte[] bytesForDprivateSubWhat(String sub_what) throws Exception { + return bytesForSomeDsubSomethingSomeAccess("p/D", sub_what, ACC_PRIVATE); + } /** - * The bytecodes for an invokeExact of a particular methodHandle, I.m, invoked on a D + * Returns the bytes for a class with name d_name (presumably "D" in some + * package), extending some class with name sub_what, implementing p.I, + * and defining two methods m() and m(11args) with access method_acc. + * + * @param d_name Name of class that is defined + * @param sub_what Name of class that it extends + * @param method_acc Accessibility of method(s) m in defined class. + * @return + * @throws Exception + */ + public static byte[] bytesForSomeDsubSomethingSomeAccess + (String d_name, String sub_what, int method_acc) + throws Exception { - class T { - T() { super(); } // boring constructor - int test() { - MethodHandle mh = `I.m():int`; - D d = new D(); - return mh.invokeExact(d); // Should explode here, AbstractMethodError - } + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES + | ClassWriter.COMPUTE_MAXS); + MethodVisitor mv; + String[] interfaces = {"p/I"}; + + cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, d_name, null, sub_what, interfaces); + { + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, sub_what, "", "()V"); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + // int m() {return 3;} + { + mv = cw.visitMethod(method_acc, "m", "()I", null, null); + mv.visitCode(); + mv.visitLdcInsn(new Integer(3)); + mv.visitInsn(IRETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + // int m(11args) {return 3;} + { + mv = cw.visitMethod(method_acc, "m", "(BCSIJ" + + "Ljava/lang/Object;" + + "Ljava/lang/Object;" + + "Ljava/lang/Object;" + + "Ljava/lang/Object;" + + "Ljava/lang/Object;" + + "Ljava/lang/Object;" + + ")I", null, null); + mv.visitCode(); + mv.visitLdcInsn(new Integer(3)); + mv.visitInsn(IRETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); } + cw.visitEnd(); + return cw.toByteArray(); + } + /** + * The bytecodes for a class p/T defining a methods test() and test(11args) + * that contain an invokeExact of a particular methodHandle, I.m. + * + * Test will be passed values that may imperfectly implement I, + * and thus may in turn throw exceptions. + * * @return * @throws Exception */ public static byte[] bytesForT() throws Exception { - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES + | ClassWriter.COMPUTE_MAXS); MethodVisitor mv; - cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "T", null, "java/lang/Object", null); + cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "p/T", null, "java/lang/Object", null); { mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V"); mv.visitInsn(RETURN); - mv.visitMaxs(0,0); + mv.visitMaxs(0, 0); mv.visitEnd(); } + // static int test(I) { - mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "test", "()I", null, null); + mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "test", "(Lp/I;)I", null, null); mv.visitCode(); - mv.visitLdcInsn(new Handle(Opcodes.H_INVOKEINTERFACE, "I", "m", "()I")); - mv.visitTypeInsn(NEW, "D"); - mv.visitInsn(DUP); - mv.visitMethodInsn(INVOKESPECIAL, "D", "", "()V"); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact", "(LI;)I"); + mv.visitLdcInsn(new Handle(Opcodes.H_INVOKEINTERFACE, "p/I", "m", "()I")); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", + "invokeExact", "(Lp/I;)I"); mv.visitInsn(IRETURN); - mv.visitMaxs(0,0); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + // static int test(I,11args) + { + mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "test", "(Lp/I;BCSIJLjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I", null, null); + mv.visitCode(); + mv.visitLdcInsn(new Handle(Opcodes.H_INVOKEINTERFACE, "p/I", "m", "(BCSIJLjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I")); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ILOAD, 1); + mv.visitVarInsn(ILOAD, 2); + mv.visitVarInsn(ILOAD, 3); + mv.visitVarInsn(ILOAD, 4); + mv.visitVarInsn(LLOAD, 5); + mv.visitVarInsn(ALOAD, 7); + mv.visitVarInsn(ALOAD, 8); + mv.visitVarInsn(ALOAD, 9); + mv.visitVarInsn(ALOAD, 10); + mv.visitVarInsn(ALOAD, 11); + mv.visitVarInsn(ALOAD, 12); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", + "invokeExact", "(Lp/I;BCSIJLjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I"); + mv.visitInsn(IRETURN); + mv.visitMaxs(0, 0); mv.visitEnd(); } cw.visitEnd(); return cw.toByteArray(); } - public static void main(String args[] ) throws Throwable { - ByteClassLoader bcl = new ByteClassLoader(); - Class d = bcl.loadBytes("D", bytesForD()); - Class t = bcl.loadBytes("T", bytesForT()); + private static void tryAndCheckThrown( + List lt, byte[] dBytes, String what, Class expected, String jar_name) + throws Throwable { + tryAndCheckThrown(lt, "p.D", dBytes, what, expected, jar_name); + } + + private static void tryAndCheckThrown(List lt, String d_name, byte[] dBytes, String what, Class expected, String jar_name) + throws Throwable { + + System.out.println("Methodhandle invokeExact I.m() for instance of " + what); + ByteClassLoader bcl1 = new ByteClassLoader(jar_name, readJarFiles, writeJarFiles); + try { + Class d1 = bcl1.loadBytes(d_name, dBytes); + Class t1 = bcl1.loadBytes("p.T", bytesForT()); + invokeTest(t1, d1, expected, lt); + } finally { + // Not necessary for others -- all class files are written in this call. + // (unless the VM crashes first). + bcl1.close(); + } + + System.out.println("Reflection invoke I.m() for instance of " + what); + ByteClassLoader bcl3 = new ByteClassLoader(jar_name, readJarFiles, false); + Class d3 = bcl3.loadBytes(d_name, dBytes); + Class t3 = bcl3.loadClass("p.Treflect"); + invokeTest(t3, d3, expected, lt); + + System.out.println("Bytecode invokeInterface I.m() for instance of " + what); + ByteClassLoader bcl2 = new ByteClassLoader(jar_name, readJarFiles, false); + Class d2 = bcl2.loadBytes(d_name, dBytes); + Class t2 = bcl2.loadClass("p.Tdirect"); + badGoodBadGood(t2, d2, expected, lt); + } + + private static void invokeTest(Class t, Class d, Class expected, List lt) + throws Throwable { + try { + Method m = t.getMethod("test", p.I.class); + Object o = d.newInstance(); + Object result = m.invoke(null, o); + if (expected != null) { + System.out.println("FAIL, Expected " + expected.getName() + + " wrapped in InvocationTargetException, but nothing was thrown"); + lt.add(new Error("Exception " + expected.getName() + " was not thrown")); + } else { + System.out.println("PASS, saw expected return."); + } + } catch (InvocationTargetException e) { + Throwable th = e.getCause(); + th.printStackTrace(System.out); + if (expected != null) { + if (expected.isInstance(th)) { + System.out.println("PASS, saw expected exception (" + expected.getName() + ")."); + } else { + System.out.println("FAIL, Expected " + expected.getName() + + " wrapped in InvocationTargetException, saw " + th); + lt.add(th); + } + } else { + System.out.println("FAIL, expected no exception, saw " + th); + lt.add(th); + } + } + System.out.println(); + } + + /* Many-arg versions of above */ + private static void tryAndCheckThrownMany(List lt, byte[] dBytes, String what, Class expected) + throws Throwable { + + System.out.println("Methodhandle invokeExact I.m(11params) for instance of " + what); + ByteClassLoader bcl1 = new ByteClassLoader("p.D", readJarFiles, false); try { - Object result = t.getMethod("test").invoke(null); - System.out.println("Expected AbstractMethodError wrapped in InvocationTargetException, saw no exception"); - throw new Error("Missing expected exception"); + Class d1 = bcl1.loadBytes("p.D", dBytes); + Class t1 = bcl1.loadBytes("p.T", bytesForT()); + invokeTestMany(t1, d1, expected, lt); + } finally { + bcl1.close(); // Not necessary for others -- all class files are written in this call. + } + + { + System.out.println("Bytecode invokeInterface I.m(11params) for instance of " + what); + ByteClassLoader bcl2 = new ByteClassLoader("pD_m_pri_imp_pI", readJarFiles, false); + Class d2 = bcl2.loadBytes("p.D", dBytes); + Class t2 = bcl2.loadClass("p.Tdirect"); + badGoodBadGoodMany(t2, d2, expected, lt); + + } + { + System.out.println("Reflection invokeInterface I.m(11params) for instance of " + what); + ByteClassLoader bcl2 = new ByteClassLoader("pD_m_pri_imp_pI", readJarFiles, false); + Class d2 = bcl2.loadBytes("p.D", dBytes); + Class t2 = bcl2.loadClass("p.Treflect"); + invokeTestMany(t2, d2, expected, lt); + } + } + + private static void invokeTestMany(Class t, Class d, Class expected, List lt) + throws Throwable { + try { + Method m = t.getMethod("test", p.I.class, + Byte.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, + Object.class, Object.class, Object.class, + Object.class, Object.class, Object.class); + Object o = d.newInstance(); + Byte b = 1; + Character c = 2; + Short s = 3; + Integer i = 4; + Long j = 5L; + Object o1 = b; + Object o2 = c; + Object o3 = s; + Object o4 = i; + Object o5 = j; + Object o6 = "6"; + + Object result = m.invoke(null, o, b, c, s, i, j, + o1, o2, o3, o4, o5, o6); + if (expected != null) { + System.out.println("FAIL, Expected " + expected.getName() + + " wrapped in InvocationTargetException, but nothing was thrown"); + lt.add(new Error("Exception " + expected.getName() + + " was not thrown")); + } else { + System.out.println("PASS, saw expected return."); + } } catch (InvocationTargetException e) { Throwable th = e.getCause(); - if (th instanceof AbstractMethodError) { - th.printStackTrace(System.out); - System.out.println("PASS, saw expected exception (AbstractMethodError, wrapped in InvocationTargetException)."); + th.printStackTrace(System.out); + if (expected != null) { + if (expected.isInstance(th)) { + System.out.println("PASS, saw expected exception (" + + expected.getName() + ")."); + } else { + System.out.println("FAIL, Expected " + expected.getName() + + " wrapped in InvocationTargetException, saw " + th); + lt.add(th); + } } else { - System.out.println("Expected AbstractMethodError wrapped in InvocationTargetException, saw " + th); - throw th; + System.out.println("FAIL, expected no exception, saw " + th); + lt.add(th); } } + System.out.println(); + } + + /** + * This tests a peculiar idiom for tickling the bug on older VMs that lack + * methodhandles. The bug (if not fixed) acts in the following way: + * + * When a broken receiver is passed to the first execution of an invokeinterface + * bytecode, the illegal access is detected before the effects of resolution are + * cached for later use, and so repeated calls with a broken receiver will always + * throw the correct error. + * + * If, however, a good receiver is passed to the invokeinterface, the effects of + * resolution will be successfully cached. A subsequent execution with a broken + * receiver will reuse the cached information, skip the detailed resolution work, + * and instead encounter a null pointer. By convention, that is the encoding for a + * missing abstract method, and an AbstractMethodError is thrown -- not the expected + * IllegalAccessError. + * + * @param t2 Test invocation class + * @param d2 Test receiver class + * @param expected expected exception type + * @param lt list of unexpected throwables seen + */ + private static void badGoodBadGood(Class t2, Class d2, Class expected, List lt) + throws Throwable { + System.out.println(" Error input 1st time"); + invokeTest(t2, d2, expected, lt); + System.out.println(" Good input (instance of Dok)"); + invokeTest(t2, Dok.class, null, lt); + System.out.println(" Error input 2nd time"); + invokeTest(t2, d2, expected, lt); + System.out.println(" Good input (instance of Dok)"); + invokeTest(t2, Dok.class, null, lt); + } + + private static void badGoodBadGoodMany(Class t2, Class d2, Class expected, List lt) + throws Throwable { + System.out.println(" Error input 1st time"); + invokeTestMany(t2, d2, expected, lt); + System.out.println(" Good input (instance of Dok)"); + invokeTestMany(t2, Dok.class, null, lt); + System.out.println(" Error input 2nd time"); + invokeTestMany(t2, d2, expected, lt); + System.out.println(" Good input (instance of Dok)"); + invokeTestMany(t2, Dok.class, null, lt); } } diff --git a/test/compiler/jsr292/methodHandleExceptions/C.java b/test/compiler/jsr292/methodHandleExceptions/p/C.java similarity index 89% rename from test/compiler/jsr292/methodHandleExceptions/C.java rename to test/compiler/jsr292/methodHandleExceptions/p/C.java index 86678967faf930f891e5d59cce1aba7d0b9f4b97..ab0e1aa1d8d92d8daa2acefd7ed8c080d81cbb1a 100644 --- a/test/compiler/jsr292/methodHandleExceptions/C.java +++ b/test/compiler/jsr292/methodHandleExceptions/p/C.java @@ -22,12 +22,14 @@ * */ +package p; + /** * Test class -- implements I, which provides default for m, but this class * declares it abstract which (should) hide the interface default, and throw - * an abstract method error if it is called (calling it requires bytecode hacking - * or inconsistent compilation). + * an abstract method error if called. + * */ -public abstract class C implements I { +public abstract class C implements p.I { public abstract int m(); } diff --git a/test/compiler/jsr292/methodHandleExceptions/I.java b/test/compiler/jsr292/methodHandleExceptions/p/Dok.java similarity index 88% rename from test/compiler/jsr292/methodHandleExceptions/I.java rename to test/compiler/jsr292/methodHandleExceptions/p/Dok.java index f4cc27e3994627c69101226d66035d1e8392b5c1..f9fa4c2d14ae023f5e389a4d144932f394dcff52 100644 --- a/test/compiler/jsr292/methodHandleExceptions/I.java +++ b/test/compiler/jsr292/methodHandleExceptions/p/Dok.java @@ -21,7 +21,11 @@ * questions. * */ +package p; + +/** + * Test class -- implements I, extends E, both define m, so all should be well. + */ +public class Dok extends p.E { -public interface I { - default public int m() { return 1; } } diff --git a/test/compiler/jsr292/methodHandleExceptions/p/E.java b/test/compiler/jsr292/methodHandleExceptions/p/E.java new file mode 100644 index 0000000000000000000000000000000000000000..6b1f8f982e11d8587cc988d0d8597a6579f50b00 --- /dev/null +++ b/test/compiler/jsr292/methodHandleExceptions/p/E.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package p; + +/** + * Test class -- implements I, which provides default for m, but this class + * redeclares it so that all its non-overriding descendants should call its + * method instead (with no error, assuming no descendant monkey business, which + * of course is NOT usually the case in this test). + * + */ +public abstract class E implements p.I { + public int m() { + return 2; + } +} diff --git a/test/compiler/jsr292/methodHandleExceptions/p/F.java b/test/compiler/jsr292/methodHandleExceptions/p/F.java new file mode 100644 index 0000000000000000000000000000000000000000..86b6c65fd47e156a2346f96a1a7c8ee9d3054d68 --- /dev/null +++ b/test/compiler/jsr292/methodHandleExceptions/p/F.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package p; + +/** + * Test class -- implements I, which provides default for m, but this class + * redeclares it so that all its non-overriding descendants should call its + * method instead (with no error, assuming no descendant monkey business, which + * of course is NOT usually the case in this test). + * + * Note that m is final -- one form of monkey business is attempting to redefine + * m. + * + */ +public abstract class F implements p.I { + final public int m() { + return 2; + } +} diff --git a/test/compiler/jsr292/methodHandleExceptions/p/I.java b/test/compiler/jsr292/methodHandleExceptions/p/I.java new file mode 100644 index 0000000000000000000000000000000000000000..0e2847564fc029244a996fd77b0f489e0d9ec48d --- /dev/null +++ b/test/compiler/jsr292/methodHandleExceptions/p/I.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package p; + +/** + * Test interface I, provides default implementations for m() and m(11args). + */ + +public interface I { + default public int m() { return 1; } + default public int m(byte b, char c, short s, int i, long l, + Object o1, Object o2, Object o3, Object o4, Object o5, Object o6) { + return 2; + } +} diff --git a/test/compiler/jsr292/methodHandleExceptions/p/Tdirect.java b/test/compiler/jsr292/methodHandleExceptions/p/Tdirect.java new file mode 100644 index 0000000000000000000000000000000000000000..088e1fab22d84e611bc141813435177c742bb116 --- /dev/null +++ b/test/compiler/jsr292/methodHandleExceptions/p/Tdirect.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package p; + +/** + * Invokes I.m directly using invokeInterface bytecodes. + */ +public class Tdirect { + public static int test(p.I i) { + int accum = 0; + for (int j = 0; j < 100000; j++) { + accum += i.m(); + } + return accum; + } + + public static int test(p.I ii, byte b, char c, short s, int i, long l, + Object o1, Object o2, Object o3, Object o4, Object o5, Object o6) { + int accum = 0; + for (int j = 0; j < 100000; j++) { + accum += ii.m(b,c,s,i,l,o1,o2,o3,o4,o5,o6); + } + return accum; + } +} diff --git a/test/compiler/jsr292/methodHandleExceptions/p/Treflect.java b/test/compiler/jsr292/methodHandleExceptions/p/Treflect.java new file mode 100644 index 0000000000000000000000000000000000000000..a4f754aebd1e33359141b0ece9a865de2dc13b14 --- /dev/null +++ b/test/compiler/jsr292/methodHandleExceptions/p/Treflect.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package p; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Invokes I.m using reflection. + */ +public class Treflect { + + public static int test(p.I ii) throws Throwable { + int accum = 0; + Method m = p.I.class.getMethod("m"); + try { + for (int j = 0; j < 100000; j++) { + Object o = m.invoke(ii); + accum += ((Integer) o).intValue(); + } + } catch (InvocationTargetException ite) { + throw ite.getCause(); + } + return accum; + } + + public static int test(p.I ii, byte b, char c, short s, int i, long l, + Object o1, Object o2, Object o3, Object o4, Object o5, Object o6) + throws Throwable { + Method m = p.I.class.getMethod("m", Byte.TYPE, Character.TYPE, + Short.TYPE, Integer.TYPE, Long.TYPE, + Object.class, Object.class, Object.class, + Object.class, Object.class, Object.class); + int accum = 0; + try { + for (int j = 0; j < 100000; j++) { + Object o = m.invoke(ii, b, c, s, i, l, o1, o2, o3, o4, o5, o6); + accum += ((Integer) o).intValue(); + } + } catch (InvocationTargetException ite) { + throw ite.getCause(); + } + return accum; + } +}