# HG changeset patch # User goetz # Date 1529415101 -7200 # Tue Jun 19 15:31:41 2018 +0200 # Node ID bf9bb3059477044405275236f4cb53018d30ba6a # Parent c349d409262a1824865b095870b8f515a8bb1704 8199940: Print more information about class loaders in IllegalAccessErrors. Reviewed-by: 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 @@ -4528,9 +4528,14 @@ Exceptions::fthrow( THREAD_AND_LOCATION, vmSymbols::java_lang_IllegalAccessError(), - "class %s cannot access its superclass %s", + "class %s cannot access its %ssuperclass %s (%s is in loader %s; %s is in loader %s)", this_klass->external_name(), - super->external_name()); + super->is_abstract() ? "abstract " : "", + super->external_name(), + this_klass->external_name(), + this_klass->class_loader_data()->loader_name_and_id(), + super->external_name(), + super->class_loader_data()->loader_name_and_id()); } else { // Add additional message content. Exceptions::fthrow( @@ -4562,9 +4567,13 @@ Exceptions::fthrow( THREAD_AND_LOCATION, vmSymbols::java_lang_IllegalAccessError(), - "class %s cannot access its superinterface %s", + "class %s cannot access its superinterface %s (%s is in loader %s; %s is in loader %s)", this_klass->external_name(), - k->external_name()); + k->external_name(), + this_klass->external_name(), + this_klass->class_loader_data()->loader_name_and_id(), + k->external_name(), + k->class_loader_data()->loader_name_and_id()); } 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 } @@ -310,9 +310,13 @@ Exceptions::fthrow( THREAD_AND_LOCATION, vmSymbols::java_lang_IllegalAccessError(), - "failed to access class %s from class %s", + "failed to access class %s from class %s (%s is in loader %s; %s is in loader %s)", base_klass->external_name(), - ref_klass->external_name()); + ref_klass->external_name(), + base_klass->external_name(), + base_klass->class_loader_data()->loader_name_and_id(), + ref_klass->external_name(), + ref_klass->class_loader_data()->loader_name_and_id()); } else { // Use module specific message returned by verify_class_access_msg(). Exceptions::fthrow( @@ -543,7 +547,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 +595,18 @@ Exceptions::fthrow( THREAD_AND_LOCATION, vmSymbols::java_lang_IllegalAccessError(), - "tried to access method %s.%s%s from class %s", + "class %s tried to access %s%s%smethod %s.%s%s (%s is in loader %s; %s is in loader %s)", + ref_klass->external_name(), + sel_method->is_abstract() ? "abstract " : "", + sel_method->is_protected() ? "protected " : "", + sel_method->is_private() ? "private " : "", sel_klass->external_name(), sel_method->name()->as_C_string(), sel_method->signature()->as_C_string(), - ref_klass->external_name() - ); + ref_klass->external_name(), + ref_klass->class_loader_data()->loader_name_and_id(), + sel_klass->external_name(), + sel_klass->class_loader_data()->loader_name_and_id()); return; } } @@ -915,11 +925,16 @@ Exceptions::fthrow( THREAD_AND_LOCATION, vmSymbols::java_lang_IllegalAccessError(), - "tried to access field %s.%s from class %s", + "class %s tried to access %s%sfield %s.%s (%s is in loader %s; %s is in loader %s)", + ref_klass->external_name(), + fd.is_protected() ? "protected " : "", + fd.is_private() ? "private " : "", sel_klass->external_name(), fd.name()->as_C_string(), - ref_klass->external_name() - ); + ref_klass->external_name(), + ref_klass->class_loader_data()->loader_name_and_id(), + sel_klass->external_name(), + sel_klass->class_loader_data()->loader_name_and_id()); 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 @@ -541,6 +541,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,405 @@ +/* + * 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_1 = + "class test.IAE1_B cannot access its superinterface test.IAE1_A " + + "(test.IAE1_B is in loader test.IAE_Loader1 @"; + private static String expectedErrorMessage1a_2 = + "; test.IAE1_A is in loader 'app')"; + private static String expectedErrorMessage1b_1 = + "class test.IAE1_B cannot access its superinterface test.IAE1_A " + + "(test.IAE1_B is in loader 'someCLName1' @"; + private static String expectedErrorMessage1b_2 = + "; test.IAE1_A is in loader 'app')"; + + // abstract class + private static String expectedErrorMessage2_1 = + "class test.IAE2_B cannot access its abstract superclass test.IAE2_A " + + "(test.IAE2_B is in loader 'someCLName2' @"; + private static String expectedErrorMessage2_2 = + "; test.IAE2_A is in loader 'app')"; + + // class + private static String expectedErrorMessage3_1 = + "class test.IAE3_B cannot access its superclass test.IAE3_A " + + "(test.IAE3_B is in loader 'someCLName3' @"; + private static String expectedErrorMessage3_2 = + "; test.IAE3_A is in loader 'app')"; + + public static void test123(String loaderName, + String expectedErrorMessage_1, + String expectedErrorMessage_2, + 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.contains(expectedErrorMessage_1) && + errorMsg.contains(expectedErrorMessage_2))) { + System.out.println("Expected: " + expectedErrorMessage_1 + "@id " + expectedErrorMessage_2 +"\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 = + "class test.Runner4 tried to access private method test.IllegalAccessErrorTest.iae4_m()V " + + "(test.Runner4 is in loader 'app'; test.IllegalAccessErrorTest is in loader 'app')"; + + // 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 = + "class test.Runner5 tried to access private field test.IllegalAccessErrorTest.iae5_f " + + "(test.Runner5 is in loader 'app'; test.IllegalAccessErrorTest is in loader 'app')"; + + // 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 test.IAE6_B " + + "(test.IAE6_A is in loader 'app'; test.IAE6_B is in loader 'test6_class_CL' @"; + + 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.contains(expectedErrorMessage6)) { + System.out.println("Expected: " + expectedErrorMessage6 + "id)\n" + + "but got: " + errorMsg); + throw new RuntimeException("Wrong error message of IllegalAccessError."); + } + System.out.println("Passed with message: " + errorMsg); + } + } + + private static String expectedErrorMessage7_1 = + "class test.IAE78_B tried to access method test.IAE78_A.()V " + + "(test.IAE78_B is in loader 'test7_method_CL' @"; + private static String expectedErrorMessage7_2 = + "; test.IAE78_A is in loader 'app')"; + + // 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.contains(expectedErrorMessage7_1) && + errorMsg.contains(expectedErrorMessage7_2))) { + System.out.println("Expected: " + expectedErrorMessage7_1 + "id" + expectedErrorMessage7_2 + "\n" + + "but got: " + errorMsg); + throw new RuntimeException("Wrong error message of IllegalAccessError."); + } + System.out.println("Passed with message: " + errorMsg); + } + } + + private static String expectedErrorMessage8_1 = + "class test.IAE78_B tried to access field test.IAE78_A.f " + + "(test.IAE78_B is in loader 'test8_field_CL' @"; + private static String expectedErrorMessage8_2 = + "; test.IAE78_A is in loader 'app')"; + + // 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.contains(expectedErrorMessage8_1) && + errorMsg.contains(expectedErrorMessage8_2))) { + System.out.println("Expected: " + expectedErrorMessage8_1 + "id" + expectedErrorMessage8_2 + "\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_1, expectedErrorMessage1a_2, "test.IAE1_B"); // interface + test123("someCLName1", expectedErrorMessage1b_1, expectedErrorMessage1b_2, "test.IAE1_B"); // interface + test123("someCLName2", expectedErrorMessage2_1, expectedErrorMessage2_2, "test.IAE2_B"); // abstract class + test123("someCLName3", expectedErrorMessage3_1, expectedErrorMessage3_2, "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(); + } +}