8212937: Parent class loader may not have a referred ClassLoaderData instance when obtained in Klass::class_in_module_of_loader
Summary: Fix to obtain the class loader's name from the java.lang.ClassLoader object instead of its ClassLoaderData.
Reviewed-by: coleenp, mbalao, sgehwolf
--- a/src/hotspot/share/oops/klass.cpp Wed Nov 07 09:58:21 2018 -0800
+++ b/src/hotspot/share/oops/klass.cpp Wed Nov 07 13:54:22 2018 -0500
@@ -894,9 +894,18 @@
if (include_parent_loader &&
!cld->is_builtin_class_loader_data()) {
oop parent_loader = java_lang_ClassLoader::parent(class_loader());
- ClassLoaderData *parent_cld = ClassLoaderData::class_loader_data(parent_loader);
- assert(parent_cld != NULL, "parent's class loader data should not be null");
- parent_loader_name_and_id = parent_cld->loader_name_and_id();
+ ClassLoaderData *parent_cld = ClassLoaderData::class_loader_data_or_null(parent_loader);
+ // The parent loader's ClassLoaderData could be null if it is
+ // a delegating class loader that has never defined a class.
+ // In this case the loader's name must be obtained via the parent loader's oop.
+ if (parent_cld == NULL) {
+ oop cl_name_and_id = java_lang_ClassLoader::nameAndId(parent_loader);
+ if (cl_name_and_id != NULL) {
+ parent_loader_name_and_id = java_lang_String::as_utf8_string(cl_name_and_id);
+ }
+ } else {
+ parent_loader_name_and_id = parent_cld->loader_name_and_id();
+ }
parent_loader_phrase = ", parent loader ";
len += strlen(parent_loader_phrase) + strlen(parent_loader_name_and_id);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/LoaderConstraints/duplicateParentLE/ParentClassLoader.java Wed Nov 07 13:54:22 2018 -0500
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+import java.util.*;
+import java.io.*;
+
+// Delegating class loader which never defines a class.
+public class ParentClassLoader extends ClassLoader {
+ public ParentClassLoader() { super(); }
+ public ParentClassLoader(String name, ClassLoader l) { super(name, l); }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/LoaderConstraints/duplicateParentLE/PreemptingChildClassLoader.java Wed Nov 07 13:54:22 2018 -0500
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+import java.util.*;
+import java.io.*;
+
+public class PreemptingChildClassLoader extends ParentClassLoader {
+
+ private static ClassLoader p = new ParentClassLoader();
+ private final Set<String> names = new HashSet<>();
+ boolean checkLoaded = true;
+
+ public PreemptingChildClassLoader(String... names) {
+ for (String n : names) this.names.add(n);
+ }
+
+ public PreemptingChildClassLoader(String name, String[] names) {
+ super(name, p);
+ for (String n : names) this.names.add(n);
+ }
+
+ public PreemptingChildClassLoader(String name, String[] names, boolean cL) {
+ super(name, p);
+ for (String n : names) this.names.add(n);
+ checkLoaded = cL;
+ }
+
+ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (!names.contains(name)) return super.loadClass(name, resolve);
+ Class<?> result = checkLoaded ? findLoadedClass(name) : null;
+ if (result == null) {
+ String filename = name.replace('.', '/') + ".class";
+ 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;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/LoaderConstraints/duplicateParentLE/Test.java Wed Nov 07 13:54:22 2018 -0500
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+/**
+ * @test
+ * @bug 8199852
+ * @summary Test exception messages of LinkageError with a parent class loader
+ * that is not known to the VM. A class loader loads
+ * twice the same class. Should trigger exception in
+ * SystemDictionary::check_constraints().
+ * @compile ../common/Foo.java
+ * @compile ../common/J.java
+ * @compile ParentClassLoader.java
+ * PreemptingChildClassLoader.java
+ * @run main/othervm Test
+ */
+
+public class Test {
+
+ // Check that all names have external formatting ('.' and not '/' in package names).
+ // Check for parent of class loader.
+ // Break expectedErrorMessage into 2 parts due to the class loader name containing
+ // the unique @<id> identity hash which cannot be compared against.
+
+ // Check that all names have external formatting ('.' and not '/' in package names).
+ // Check for name and parent of class loader. Type should be mentioned as 'class'.
+ static String expectedErrorMessage_part1 = "loader 'DuplicateParentLE_Test_Loader' @";
+ static String expectedErrorMessage_part2 = " attempted duplicate class definition for test.Foo. (test.Foo is in unnamed module of loader 'DuplicateParentLE_Test_Loader' @";
+ static String expectedErrorMessage_part3 = ", parent loader ParentClassLoader @";
+
+
+ // Test that the error message is correct when a loader constraint error is
+ // detected during vtable creation.
+ //
+ // In this test, during vtable creation for class Task, method "Task.m()LFoo;"
+ // overrides "J.m()LFoo;". But, Task's class Foo and super type J's class Foo
+ // are different. So, a LinkageError exception should be thrown because the
+ // loader constraint check will fail.
+ public static void test(String loaderName,
+ String testType) throws Exception {
+ String[] classNames = {testType};
+ ClassLoader l = new PreemptingChildClassLoader(loaderName, classNames, false);
+ l.loadClass(testType);
+ try {
+ l.loadClass(testType).newInstance();
+ throw new RuntimeException("Expected LinkageError exception not thrown");
+ } catch (LinkageError e) {
+ String errorMsg = e.getMessage();
+ if (!errorMsg.contains(expectedErrorMessage_part1) ||
+ !errorMsg.contains(expectedErrorMessage_part2) ||
+ !errorMsg.contains(expectedErrorMessage_part3)) {
+ System.out.println("Expected: " + expectedErrorMessage_part1 + "<id>" + expectedErrorMessage_part2 + "\n" +
+ "but got: " + errorMsg);
+ throw new RuntimeException("Wrong LinkageError exception thrown: " + errorMsg);
+ }
+ System.out.println("Passed with message: " + errorMsg);
+ }
+ }
+
+ public static void main(String args[]) throws Exception {
+ test("DuplicateParentLE_Test_Loader", "test.Foo");
+ }
+}