8212937: Parent class loader may not have a referred ClassLoaderData instance when obtained in Klass::class_in_module_of_loader
authorlfoltan
Wed, 07 Nov 2018 13:54:22 -0500
changeset 52438 de25152e5ec4
parent 52437 77ae7b76e111
child 52439 a0d2fb4d3097
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
src/hotspot/share/oops/klass.cpp
test/hotspot/jtreg/runtime/LoaderConstraints/duplicateParentLE/ParentClassLoader.java
test/hotspot/jtreg/runtime/LoaderConstraints/duplicateParentLE/PreemptingChildClassLoader.java
test/hotspot/jtreg/runtime/LoaderConstraints/duplicateParentLE/Test.java
--- 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");
+    }
+}