# HG changeset patch # User goetz # Date 1521641760 -3600 # Wed Mar 21 15:16:00 2018 +0100 # Node ID 0fa7a9bf29d0c532ca8f0defdfc605ee3d49df05 # Parent 01d27ae7a84e1b5e4092d8f994c895d2b5763152 8199940: Print more information about class loaders in IllegalAccessErrors. ReviewedBy: lfoltan diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -4526,9 +4526,10 @@ Exceptions::fthrow( THREAD_AND_LOCATION, vmSymbols::java_lang_IllegalAccessError(), - "class %s cannot access its superclass %s", - this_klass->external_name(), - super->external_name()); + "class %s cannot access its %ssuperclass %s", + this_klass->class_loader_and_module_name(), + super->is_abstract() ? "abstract " : "", + super->class_loader_and_module_name()); } else { // Add additional message content. Exceptions::fthrow( @@ -4561,8 +4562,8 @@ THREAD_AND_LOCATION, vmSymbols::java_lang_IllegalAccessError(), "class %s cannot access its superinterface %s", - this_klass->external_name(), - k->external_name()); + this_klass->class_loader_and_module_name(), + k->class_loader_and_module_name()); } else { // Add additional message content. Exceptions::fthrow( diff --git a/src/hotspot/share/interpreter/linkResolver.cpp b/src/hotspot/share/interpreter/linkResolver.cpp --- a/src/hotspot/share/interpreter/linkResolver.cpp +++ b/src/hotspot/share/interpreter/linkResolver.cpp @@ -218,7 +218,7 @@ fatal("Unexpected call kind %d", call_kind()); } } -#endif //ASSERT +#endif // ASSERT #ifndef PRODUCT void CallInfo::print() { @@ -294,7 +294,7 @@ base_klass = ObjArrayKlass::cast(sel_klass)->bottom_klass(); } // The element type could be a typeArray - we only need the access - // check if it is an reference to another class. + // check if it is a reference to another class. if (!base_klass->is_instance_klass()) { return; // no relevant check to do } @@ -311,8 +311,8 @@ THREAD_AND_LOCATION, vmSymbols::java_lang_IllegalAccessError(), "failed to access class %s from class %s", - base_klass->external_name(), - ref_klass->external_name()); + base_klass->class_loader_and_module_name(), + ref_klass->class_loader_and_module_name()); } else { // Use module specific message returned by verify_class_access_msg(). Exceptions::fthrow( @@ -543,7 +543,7 @@ } assert(actual_size_of_params == expected_size_of_params, "%d != %d", actual_size_of_params, expected_size_of_params); -#endif //ASSERT +#endif // ASSERT assert(appendix_result_or_null != NULL, ""); (*appendix_result_or_null) = appendix; @@ -591,12 +591,14 @@ Exceptions::fthrow( THREAD_AND_LOCATION, vmSymbols::java_lang_IllegalAccessError(), - "tried to access method %s.%s%s from class %s", - sel_klass->external_name(), + "tried to access %s%s%smethod %s.%s%s from class %s", + sel_method->is_abstract() ? "abstract " : "", + sel_method->is_protected() ? "protected " : "", + sel_method->is_private() ? "private " : "", + sel_klass->class_loader_and_module_name(), sel_method->name()->as_C_string(), sel_method->signature()->as_C_string(), - ref_klass->external_name() - ); + ref_klass->class_loader_and_module_name()); return; } } @@ -913,11 +915,12 @@ Exceptions::fthrow( THREAD_AND_LOCATION, vmSymbols::java_lang_IllegalAccessError(), - "tried to access field %s.%s from class %s", - sel_klass->external_name(), + "tried to access %s%sfield %s.%s from class %s", + fd.is_protected() ? "protected " : "", + fd.is_private() ? "private " : "", + sel_klass->class_loader_and_module_name(), fd.name()->as_C_string(), - ref_klass->external_name() - ); + ref_klass->class_loader_and_module_name()); return; } } diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp --- a/src/hotspot/share/oops/klass.hpp +++ b/src/hotspot/share/oops/klass.hpp @@ -542,6 +542,9 @@ // and the package separators as '/'. virtual const char* signature_name() const; + // Returns a descriptive string as "//class name". + // This is the format used in stack traces and similar. Based on + // external_name(). const char* class_loader_and_module_name() const; // Returns "interface", "abstract class" or "class". diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE78_A.java b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE78_A.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE78_A.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 by SAP AG, Walldorf, Germany. + * 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 test; + +public class IAE78_A { + + static Object f = new Object(); + + IAE78_A() { + // Nothing to do. + } +} diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE78_B.java b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE78_B.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE78_B.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 SAP SE. 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 test; + +/** + * Helper classs. + */ +public class IAE78_B { + + public static void create() { + new IAE78_A(); + } + + public static void access() { + IAE78_A.f.hashCode(); + } +} diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE_Loader1.java b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE_Loader1.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE_Loader1.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018, 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 test; + +import java.util.*; +import java.io.*; + +public class IAE_Loader1 extends ClassLoader { + + private final Set names = new HashSet<>(); + + public IAE_Loader1(String name, String[] names) { + super(name, ClassLoader.getSystemClassLoader()); + for (String n : names) this.names.add(n); + } + + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (!names.contains(name)) { + return super.loadClass(name, resolve); + } + String filename = name.replace('.', '/') + ".class"; + Class result = null; + try (InputStream data = getResourceAsStream(filename)) { + if (data == null) { + throw new ClassNotFoundException(); + } + try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) { + int b; + do { + b = data.read(); + if (b >= 0) buffer.write(b); + } while (b >= 0); + byte[] bytes = buffer.toByteArray(); + result = defineClass(name, bytes, 0, bytes.length); + } + } catch (IOException e) { + throw new ClassNotFoundException("Error reading class file", e); + } + if (resolve) resolveClass(result); + return result; + } + +} diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE_Loader2.java b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE_Loader2.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE_Loader2.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2018 by SAP AG, Walldorf, Germany. + * 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 test; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.SecureClassLoader; +import java.util.Arrays; +import java.util.HashSet; + +/** + * This is a class loader which can load the same classes as another class loader. + *

+ * This is mainly useful for tests when you want to load a class, but do it with a class + * loader you can dispose. The clone loader just asks the loader to be cloned to get + * the bytecodes, but defines the class itself. + *

+ * Additionally you can specify a set of classes the loader should not be able to load. + */ +public class IAE_Loader2 extends SecureClassLoader { + + /** + * The class loaded to clone. + */ + private final ClassLoader toClone; + + /** + * The strings we cannot load. + */ + private final HashSet notLoadable; + + /** + * The strings we just delegate. + */ + private final HashSet simpleDelegate; + + /** + * Creates a class loader which can load the same classes as the loader which + * loaded the IAE_Loader2 class itself. + *

+ * Only the classes which are loadable by the 'parent' loader are delegated to that + * loader (to make it possible mix classes). + * + * @param name the name of the class loader. + * @param parent the parent loader which is first asked to load a class. + * @param toClone the class loader to mimic. The clone class loader will be able to + * load the same classes as the 'toClone' loader. + * @param notLoadable The classes we should not be able to load via this loader. + * @param simpleDelegate The names of the classes for which we simply delegate. + */ + public IAE_Loader2(String name, ClassLoader parent, ClassLoader toClone, String[] notLoadable, + String[] simpleDelegate) { + super(name, parent); + + this.toClone = toClone; + this.notLoadable = new HashSet<>(Arrays.asList(notLoadable)); + this.simpleDelegate = new HashSet<>(Arrays.asList(simpleDelegate)); + } + + /** + * @see java.lang.ClassLoader#findClass(java.lang.String) + */ + @Override + protected Class findClass(String name) throws ClassNotFoundException { + if (notLoadable.contains(name)) { + throw new ClassNotFoundException("The clone class loader explicitely " + + "didn't found the class"); + } + + if (simpleDelegate.contains(name)) { + return toClone.loadClass(name); + } + + // We just ask the wrapper class loader to find the resource for us + URL res = toClone.getResource(name.replace('.', '/') + ".class"); + + if (res == null) { + throw new ClassNotFoundException(name); + } + + try { + InputStream is = res.openStream(); + byte[] code = readStreamIntoBuffer(is, 8192); + is.close(); + return defineClass(name, code, 0, code.length); + } catch (IOException e) { + throw new ClassNotFoundException(name, e); + } + } + + /** + * Reads all data of a stream into a byte array. The method allocates as + * much memory as necessary to put the whole data into that byte + * array. The data is read in chunks of chunkSize + * chunks.

+ * Implementation Note: The data is read in chunks of + * chunkSize bytes. The data is copied to the result + * array. The memory consumption at the end of the reading is + * 2 x [size of resulting array] + chunkSize. + * + * @param is the stream to read the data from + * @param chunkSize the size of the chunks the data should be read in + * @return the whole data of the stream read into an byte array + * @throws IllegalArgumentException if chunkSize <= 0 + * @throws NullPointerException if is == null + * @throws IOException thrown if the provided stream encounters IO problems + */ + public static byte[] readStreamIntoBuffer(InputStream is, int chunkSize) + throws IOException { + + // Check preconditions. + if (chunkSize <= 0) { + throw new IllegalArgumentException("chunkSize <= 0"); + } + else if (is == null) { + throw new NullPointerException("is is null"); + } + + // Temporary buffer for read operations and result buffer. + byte[] tempBuffer = new byte[chunkSize]; + byte[] buffer = new byte[0]; + + int bytesRead = 0; // bytes actual read + int oldSize = 0; // size of the resulting buffer + + while ((bytesRead = is.read(tempBuffer)) > 0) { + + // Temporary reference to the buffer for the copy operation. + byte[] oldBuffer = buffer; + + // Create a new buffer with the size needed and copy data. + buffer = new byte[oldSize + bytesRead]; + System.arraycopy(oldBuffer, 0, buffer, 0, oldBuffer.length); + + // Copy the new data. + System.arraycopy(tempBuffer, 0, buffer, oldSize, bytesRead); + oldSize += bytesRead; + } + + return buffer; + } +} diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IllegalAccessErrorTest.java b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IllegalAccessErrorTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IllegalAccessErrorTest.java @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 SAP SE. 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. + * + */ + +/** + * @test + * @summary Test messages of IllegalAccessError. + * @modules java.base/java.lang:open + * java.base/jdk.internal.org.objectweb.asm + * @compile IAE_Loader1.java IAE_Loader2.java IAE78_A.java IAE78_B.java + * IllegalAccessErrorTest.java + * @run main/othervm -Xbootclasspath/a:. test.IllegalAccessErrorTest + */ + +// Put this test into a package so we see qualified class names in +// the error messages. Verify that classes are printed with '.' instead +// of '/'. +package test; + +import java.lang.reflect.*; +import java.lang.invoke.MethodHandles.Lookup; +import static java.lang.invoke.MethodHandles.*; +import static java.lang.invoke.MethodHandles.Lookup.*; +import java.security.*; + +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import static jdk.internal.org.objectweb.asm.Opcodes.*; + +import test.*; + +abstract public class IllegalAccessErrorTest { + + // interface + private static String expectedErrorMessage1a = + "class test.IAE1_B cannot access its superinterface test.IAE1_A"; + private static String expectedErrorMessage1b = + "class someCLName1//test.IAE1_B cannot access its superinterface test.IAE1_A"; + + // abstract class + private static String expectedErrorMessage2 = + "class someCLName2//test.IAE2_B cannot access its abstract superclass test.IAE2_A"; + + // class + private static String expectedErrorMessage3 = + "class someCLName3//test.IAE3_B cannot access its superclass test.IAE3_A"; + + public static void test123(String loaderName, + String expectedErrorMessage, + String testClass) throws Exception { + String[] classNames = { testClass }; + // Some classes under a new Loader. + ClassLoader l = new IAE_Loader1(loaderName, classNames); + + try { + l.loadClass(testClass); + throw new RuntimeException("Expected IllegalAccessError was not thrown."); + } catch (IllegalAccessError iae) { + String errorMsg = iae.getMessage(); + if (!errorMsg.equals(expectedErrorMessage)) { + System.out.println("Expected: " + expectedErrorMessage + "\n" + + "but got: " + errorMsg); + throw new RuntimeException("Wrong error message of IllegalAccessError."); + } else { + System.out.println("Passed with message: " + errorMsg); + } + } + } + + // Generate a class file with the given class name. The class implements Runnable + // with a run method to invokestatic the given targetClass/targetMethod. + static byte[] iae4_generateRunner(String className, + String targetClass, + String targetMethod) throws Exception { + + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + + ClassWriter.COMPUTE_FRAMES); + cw.visit(V9, + ACC_PUBLIC + ACC_SUPER, + className.replace(".", "/"), + null, + "java/lang/Object", + new String[] { "java/lang/Runnable" }); + + // + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + // run() + String tc = targetClass.replace(".", "/"); + mv = cw.visitMethod(ACC_PUBLIC, "run", "()V", null, null); + mv.visitMethodInsn(INVOKESTATIC, tc, targetMethod, "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + cw.visitEnd(); + return cw.toByteArray(); + } + + // Private method that should raise IllegalAccessError when called. + private static void iae4_m() { } + + private static String expectedErrorMessage4 = + "tried to access private method test.IllegalAccessErrorTest.iae4_m()V from class test.Runner4"; + + // Test according to java/lang/invoke/DefineClassTest.java + public static void test4_privateMethod() throws Exception { + final String THIS_PACKAGE = IllegalAccessErrorTest.class.getPackageName(); + final String THIS_CLASS = IllegalAccessErrorTest.class.getName(); + final String CLASS_NAME = THIS_PACKAGE + ".Runner4"; + Lookup lookup = lookup(); + + // private + byte[] classBytes = iae4_generateRunner(CLASS_NAME, THIS_CLASS, "iae4_m"); + Class clazz = lookup.defineClass(classBytes); + Runnable r = (Runnable) clazz.getDeclaredConstructor().newInstance(); + try { + r.run(); + throw new RuntimeException("Expected IllegalAccessError was not thrown."); + } catch (IllegalAccessError exc) { + String errorMsg = exc.getMessage(); + if (!errorMsg.equals(expectedErrorMessage4)) { + System.out.println("Expected: " + expectedErrorMessage4 + "\n" + + "but got: " + errorMsg); + throw new RuntimeException("Wrong error message of IllegalAccessError."); + } + System.out.println("Passed with message: " + errorMsg); + } + } + + // Generate a class file with the given class name. The class implements Runnable + // with a run method to invokestatic the given targetClass/targetField. + static byte[] iae5_generateRunner(String className, + String targetClass, + String targetField) throws Exception { + + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + + ClassWriter.COMPUTE_FRAMES); + cw.visit(V9, + ACC_PUBLIC + ACC_SUPER, + className.replace(".", "/"), + null, + "java/lang/Object", + new String[] { "java/lang/Runnable" }); + + // + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + // run() + String tc = targetClass.replace(".", "/"); + mv = cw.visitMethod(ACC_PUBLIC, "run", "()V", null, null); + mv.visitFieldInsn(GETSTATIC, tc, targetField, "I"); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + cw.visitEnd(); + return cw.toByteArray(); + } + + // Private field that should raise IllegalAccessError when accessed. + private static int iae5_f = 77; + + private static String expectedErrorMessage5 = + "tried to access private field test.IllegalAccessErrorTest.iae5_f from class test.Runner5"; + + // Test according to java/lang/invoke/DefineClassTest.java + public static void test5_privateField() throws Exception { + final String THIS_PACKAGE = IllegalAccessErrorTest.class.getPackageName(); + final String THIS_CLASS = IllegalAccessErrorTest.class.getName(); + final String CLASS_NAME = THIS_PACKAGE + ".Runner5"; + Lookup lookup = lookup(); + + // private + byte[] classBytes = iae5_generateRunner(CLASS_NAME, THIS_CLASS, "iae5_f"); + Class clazz = lookup.defineClass(classBytes); + Runnable r = (Runnable) clazz.getDeclaredConstructor().newInstance(); + try { + r.run(); + throw new RuntimeException("Expected IllegalAccessError was not thrown."); + } catch (IllegalAccessError exc) { + String errorMsg = exc.getMessage(); + if (!errorMsg.equals(expectedErrorMessage5)) { + System.out.println("Expected: " + expectedErrorMessage5 + "\n" + + "but got: " + errorMsg); + throw new RuntimeException("Wrong error message of IllegalAccessError."); + } + System.out.println("Passed with message: " + errorMsg); + } + } + + private static String expectedErrorMessage6 = + "failed to access class test.IAE6_A from class test6_class_CL//test.IAE6_B"; + + public static void test6_class() throws Exception { + ClassLoader base = IllegalAccessErrorTest.class.getClassLoader(); + IAE_Loader2 loader = new IAE_Loader2("test6_class_CL", base.getParent(), base, new String[0], + new String[] { IAE6_A.class.getName() }); + Class cl = loader.loadClass(IAE6_B.class.getName()); + Method m = cl.getDeclaredMethod("create", new Class[0]); + m.setAccessible(true); + + try { + m.invoke(null, new Object[0]); + throw new RuntimeException("Expected IllegalAccessError was not thrown."); + } catch (InvocationTargetException e) { + IllegalAccessError iae = (IllegalAccessError) e.getCause(); + String errorMsg = iae.getMessage(); + if (!errorMsg.equals(expectedErrorMessage6)) { + System.out.println("Expected: " + expectedErrorMessage6 + "\n" + + "but got: " + errorMsg); + throw new RuntimeException("Wrong error message of IllegalAccessError."); + } + System.out.println("Passed with message: " + errorMsg); + } + } + + private static String expectedErrorMessage7 = + "tried to access method test.IAE78_A.()V from class test7_method_CL//test.IAE78_B"; + + // Similar to test4. + public static void test7_method() throws Exception { + ClassLoader base = IllegalAccessErrorTest.class.getClassLoader(); + IAE_Loader2 loader = new IAE_Loader2("test7_method_CL", base.getParent(), base, new String[0], + new String[] {IAE78_A.class.getName()}); + Class cl = loader.loadClass(IAE78_B.class.getName()); + Method m = cl.getDeclaredMethod("create", new Class[0]); + + try { + m.invoke(null, new Object[0]); + } catch (InvocationTargetException e) { + IllegalAccessError iae = (IllegalAccessError) e.getCause(); + String errorMsg = iae.getMessage(); + if (!errorMsg.equals(expectedErrorMessage7)) { + System.out.println("Expected: " + expectedErrorMessage7 + "\n" + + "but got: " + errorMsg); + throw new RuntimeException("Wrong error message of IllegalAccessError."); + } + System.out.println("Passed with message: " + errorMsg); + } + } + + private static String expectedErrorMessage8 = + "tried to access field test.IAE78_A.f from class test8_field_CL//test.IAE78_B"; + + // Similar to test5. + public static void test8_field() throws Exception { + ClassLoader base = IllegalAccessErrorTest.class.getClassLoader(); + IAE_Loader2 loader = new IAE_Loader2("test8_field_CL", base.getParent(), base, new String[0], + new String[] { IAE78_A.class.getName() }); + Class cl = loader.loadClass(IAE78_B.class.getName()); + Method m = cl.getDeclaredMethod("access", new Class[0]); + + try { + m.invoke(null, new Object[0]); + } + catch (InvocationTargetException e) { + IllegalAccessError iae = (IllegalAccessError) e.getCause(); + String errorMsg = iae.getMessage(); + if (!errorMsg.equals(expectedErrorMessage8)) { + System.out.println("Expected: " + expectedErrorMessage8 + "\n" + + "but got: " + errorMsg); + throw new RuntimeException("Wrong error message of IllegalAccessError."); + } + System.out.println("Passed with message: " + errorMsg); + } + } + + public static void main(String[] args) throws Exception { + test123(null, expectedErrorMessage1a, "test.IAE1_B"); // interface + test123("someCLName1", expectedErrorMessage1b, "test.IAE1_B"); // interface + test123("someCLName2", expectedErrorMessage2, "test.IAE2_B"); // abstract class + test123("someCLName3", expectedErrorMessage3, "test.IAE3_B"); // class + test4_privateMethod(); + test5_privateField(); + test6_class(); + test7_method(); + test8_field(); + } +} + +// Class hierarchies for test1. +interface IAE1_A { + public IAE1_D gen(); +} + +class IAE1_B implements IAE1_A { + public IAE1_D gen() { + return null; + } +} + +abstract class IAE1_C { +} + +class IAE1_D extends IAE1_C { +} + + +// Class hierarchies for test2. +abstract class IAE2_A { + abstract public IAE2_D gen(); +} + +class IAE2_B extends IAE2_A { + public IAE2_D gen() { + return null; + } +} + +abstract class IAE2_C { +} + +class IAE2_D extends IAE2_C { +} + + +// Class hierarchies for test3. +class IAE3_A { + public IAE3_D gen() { + return null; + }; +} + +class IAE3_B extends IAE3_A { + public IAE3_D gen() { + return null; + } +} + +abstract class IAE3_C { +} + +class IAE3_D extends IAE3_C { +} + + +// Class hierarchies for test6. +class IAE6_A { + IAE6_A() { + // Nothing to do. + } +} + +class IAE6_B { + public static void create() { + new IAE6_A(); + } +}