# HG changeset patch # User goetz # Date 1521540963 -3600 # Tue Mar 20 11:16:03 2018 +0100 # Node ID 3455247e362013829396e2e5387d8ad76139516d # Parent 1708db7f94c6ef231509de0fdeb561e4d27deebc 8199852: Print more information about class loaders in LinkageErrors. diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -4107,6 +4107,47 @@ return loader->obj_field(unnamedModule_offset); } +// Caller needs ResourceMark. +const char* java_lang_ClassLoader::describe_external(const oop loader) { + if (loader == NULL) { + return ""; + } + + const char* name = NULL; + oop nameOop = java_lang_ClassLoader::name(loader); + if (nameOop != NULL) { + name = java_lang_String::as_utf8_string(nameOop); + } + + stringStream ss; + const char *seperator; + // If we have a name set for this loader, display it. Otherwise use + // the class of the loader. + if (name != NULL) { + ss.print("\"%s\" (instance of %s", name, loader->klass()->external_name()); + seperator = ", "; + } else { + ss.print("%s", loader->klass()->external_name()); + seperator = " ("; + } + + oop pl = java_lang_ClassLoader::parent(loader); + if (pl != NULL) { + ss.print("%sparent: ", seperator); + oop parentNameOop = java_lang_ClassLoader::name(pl); + if (parentNameOop != NULL) { + const char* parentName = java_lang_String::as_utf8_string(parentNameOop); + if (parentName != NULL) { + ss.print("\"%s\" ", parentName); + } + } + ss.print("%s)", pl->klass()->external_name()); + } else { + ss.print("%sparent: )", seperator); + } + return ss.as_string(); +} + // Support for java_lang_System // #define SYSTEM_FIELDS_DO(macro) \ diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -1305,6 +1305,11 @@ // Debugging friend class JavaClasses; friend class ClassFileParser; // access to number_of_fake_fields + + // Describe ClassLoader for exceptions, tracing ... + // Prints "" (instance of , parent: "" ) + // or (parent: "" ). + static const char* describe_external(const oop loader); }; diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -2142,10 +2142,15 @@ void SystemDictionary::check_constraints(unsigned int d_hash, InstanceKlass* k, - Handle class_loader, bool defining, + Handle class_loader, + bool defining, TRAPS) { const char *linkage_error1 = NULL; const char *linkage_error2 = NULL; + const char *linkage_error3 = ""; + // Remember the loader of the similar class that is already loaded. + const char *existing_klass_loader_name = ""; + { Symbol* name = k->name(); ClassLoaderData *loader_data = class_loader_data(class_loader); @@ -2154,16 +2159,17 @@ InstanceKlass* check = find_class(d_hash, name, loader_data->dictionary()); if (check != NULL) { - // if different InstanceKlass - duplicate class definition, - // else - ok, class loaded by a different thread in parallel, - // we should only have found it if it was done loading and ok to use - // dictionary only holds instance classes, placeholders - // also holds array classes + // If different InstanceKlass - duplicate class definition, + // else - ok, class loaded by a different thread in parallel. + // We should only have found it if it was done loading and ok to use. + // The dictionary only holds instance classes, placeholders + // also hold array classes. assert(check->is_instance_klass(), "noninstance in systemdictionary"); if ((defining == true) || (k != check)) { - linkage_error1 = "loader (instance of "; - linkage_error2 = "): attempted duplicate class definition for name: \""; + linkage_error1 = "loader "; + linkage_error2 = " attempted duplicate class definition for "; + guarantee(check->class_loader() == class_loader(), "Per construction. Else report the other loader."); } else { return; } @@ -2176,8 +2182,14 @@ if (linkage_error1 == NULL) { if (constraints()->check_or_update(k, class_loader, name) == false) { - linkage_error1 = "loader constraint violation: loader (instance of "; - linkage_error2 = ") previously initiated loading for a different type with name \""; + linkage_error1 = "loader constraint violation: loader "; + linkage_error2 = " wants to load class "; + linkage_error3 = ". A different class with the same name was previously loaded by "; + Klass *existing_klass = constraints()->find_constrained_klass(name, class_loader); + if (existing_klass->class_loader() != class_loader()) { + existing_klass_loader_name = + java_lang_ClassLoader::describe_external(existing_klass->class_loader()); + } } } } @@ -2187,17 +2199,18 @@ if (linkage_error1) { ResourceMark rm(THREAD); - const char* class_loader_name = loader_name(class_loader()); - char* type_name = k->name()->as_C_string(); - size_t buflen = strlen(linkage_error1) + strlen(class_loader_name) + - strlen(linkage_error2) + strlen(type_name) + 2; // +2 for '"' and null byte. - char* buf = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, buflen); - jio_snprintf(buf, buflen, "%s%s%s%s\"", linkage_error1, class_loader_name, linkage_error2, type_name); - THROW_MSG(vmSymbols::java_lang_LinkageError(), buf); + stringStream ss; + ss.print("%s", linkage_error1); + ss.print("%s", java_lang_ClassLoader::describe_external(class_loader())); + ss.print("%s", linkage_error2); + ss.print("%s", k->external_name()); + ss.print("%s", linkage_error3); + ss.print("%s", existing_klass_loader_name); + ss.print("."); + THROW_MSG(vmSymbols::java_lang_LinkageError(), ss.as_string()); } } - // Update class loader data dictionary - done after check_constraint and add_to_hierachy // have been called. void SystemDictionary::update_dictionary(unsigned int d_hash, @@ -3093,10 +3106,18 @@ NOT_PRODUCT(SystemDictionary::verify()); } -// caller needs ResourceMark +// Return string to give helpful intformation about a classloader. +// Caller needs ResourceMark. const char* SystemDictionary::loader_name(const oop loader) { - return ((loader) == NULL ? "" : - InstanceKlass::cast((loader)->klass())->name()->as_C_string()); +#if INCLUDE_CDS + if (DumpSharedSpaces) { + // Describe_external() calls assert(is_instance...) in various places, + // which may fail with DumpSharedSpaces. + if ((loader) == NULL) return ""; + return InstanceKlass::cast((loader)->klass())->name()->as_C_string(); + } +#endif + return java_lang_ClassLoader::describe_external(loader); } // caller needs ResourceMark 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 @@ -655,13 +655,13 @@ resolved_loader, true, CHECK); if (failed_type_symbol != NULL) { const char* msg = "loader constraint violation: when resolving %s" - " \"%s\" the class loader (instance of %s) of the current class, %s," - " and the class loader (instance of %s) for the method's defining class, %s, have" + " \"%s\" the class loader %s of the current class, %s," + " and the class loader %s for the method's defining class, %s, have" " different Class objects for the type %s used in the signature"; char* sig = link_info.method_string(); - const char* loader1_name = SystemDictionary::loader_name(current_loader()); + const char* loader1_name = java_lang_ClassLoader::describe_external(current_loader()); char* current = link_info.current_klass()->name()->as_C_string(); - const char* loader2_name = SystemDictionary::loader_name(resolved_loader()); + const char* loader2_name = java_lang_ClassLoader::describe_external(resolved_loader()); char* target = resolved_method->method_holder()->name()->as_C_string(); char* failed_type_name = failed_type_symbol->as_C_string(); size_t buflen = strlen(msg) + strlen(sig) + strlen(loader1_name) + @@ -688,13 +688,13 @@ CHECK); if (failed_type_symbol != NULL) { const char* msg = "loader constraint violation: when resolving field" - " \"%s\" the class loader (instance of %s) of the referring class, " - "%s, and the class loader (instance of %s) for the field's resolved " + " \"%s\" the class loader %s of the referring class, " + "%s, and the class loader %s for the field's resolved " "type, %s, have different Class objects for that type"; char* field_name = field->as_C_string(); - const char* loader1_name = SystemDictionary::loader_name(ref_loader()); + const char* loader1_name = java_lang_ClassLoader::describe_external(ref_loader()); char* sel = sel_klass->name()->as_C_string(); - const char* loader2_name = SystemDictionary::loader_name(sel_loader()); + const char* loader2_name = java_lang_ClassLoader::describe_external(sel_loader()); char* failed_type_name = failed_type_symbol->as_C_string(); size_t buflen = strlen(msg) + strlen(field_name) + strlen(loader1_name) + strlen(sel) + strlen(loader2_name) + strlen(failed_type_name) + 1; diff --git a/src/hotspot/share/oops/klassVtable.cpp b/src/hotspot/share/oops/klassVtable.cpp --- a/src/hotspot/share/oops/klassVtable.cpp +++ b/src/hotspot/share/oops/klassVtable.cpp @@ -504,15 +504,15 @@ CHECK_(false)); if (failed_type_symbol != NULL) { const char* msg = "loader constraint violation for class %s: when selecting " - "overriding method \"%s\" the class loader (instance of %s) of the " - "selected method's type %s, and the class loader (instance of %s) for its super " + "overriding method %s the class loader %s of the " + "selected method's type %s, and the class loader %s for its super " "type %s have different Class objects for the type %s used in the signature"; - char* curr_class = klass->name()->as_C_string(); - char* sig = target_method()->name_and_sig_as_C_string(); - const char* loader1 = SystemDictionary::loader_name(target_loader()); - char* sel_class = target_klass->name()->as_C_string(); - const char* loader2 = SystemDictionary::loader_name(super_loader()); - char* super_class = super_klass->name()->as_C_string(); + const char* curr_class = klass->external_name(); + const char* sig = target_method()->name_and_sig_as_C_string(); + const char* loader1 = java_lang_ClassLoader::describe_external(target_loader()); + const char* sel_class = target_klass->external_name(); + const char* loader2 = java_lang_ClassLoader::describe_external(super_loader()); + const char* super_class = super_klass->external_name(); char* failed_type_name = failed_type_symbol->as_C_string(); size_t buflen = strlen(msg) + strlen(curr_class) + strlen(sig) + strlen(loader1) + strlen(sel_class) + strlen(loader2) + @@ -1234,17 +1234,17 @@ true, CHECK); if (failed_type_symbol != NULL) { const char* msg = "loader constraint violation in interface itable" - " initialization for class %s: when selecting method \"%s\" the" - " class loader (instance of %s) for super interface %s, and the class" - " loader (instance of %s) of the selected method's type, %s have" + " initialization for class %s: when selecting method %s the" + " class loader %s for super interface %s, and the class" + " loader %s of the selected method's type, %s have" " different Class objects for the type %s used in the signature"; - char* current = _klass->name()->as_C_string(); - char* sig = m->name_and_sig_as_C_string(); - const char* loader1 = SystemDictionary::loader_name(interface_loader()); - char* iface = InstanceKlass::cast(interf)->name()->as_C_string(); - const char* loader2 = SystemDictionary::loader_name(method_holder_loader()); - char* mclass = target()->method_holder()->name()->as_C_string(); - char* failed_type_name = failed_type_symbol->as_C_string(); + const char* current = _klass->external_name(); + const char* sig = m->name_and_sig_as_C_string(); + const char* loader1 = java_lang_ClassLoader::describe_external(interface_loader()); + const char* iface = InstanceKlass::cast(interf)->external_name(); + const char* loader2 = java_lang_ClassLoader::describe_external(method_holder_loader()); + const char* mclass = target()->method_holder()->external_name(); + const char* failed_type_name = failed_type_symbol->as_C_string(); size_t buflen = strlen(msg) + strlen(current) + strlen(sig) + strlen(loader1) + strlen(iface) + strlen(loader2) + strlen(mclass) + strlen(failed_type_name); diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE1.java b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE1.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE1.java @@ -0,0 +1,30 @@ +/* + * 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. + */ + + +// Just a class in a package to get a qualified path. + +package test; + +class LE1 {} diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_A.java b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_A.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_A.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2010, 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. + * + */ + +// A simple interface, to allow an unknown foreign call from a class +// loaded with the bootstrap loader to a class loaded with Loader2. +public interface LE2_A { + public LE2_D_ambgs[] gen(); +} + diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_B.java b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_B.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_B.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010, 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. + * + */ + +// This class is loaded via Loader2. Using LE2_D_ambgs here will trigger +// loading it's second version with Loader2. +public class LE2_B implements LE2_A { + public LE2_D_ambgs[] gen() { + LE2_D_ambgs[] x = new LE2_D_ambgs[1]; + x[0] = new LE2_D_ambgs(); + return x; + } +} diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_C.java b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_C.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_C.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2010, 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. + * + */ + +abstract public class LE2_C { + public abstract LE2_D_ambgs[] make(LE2_A iface); // abstract factory +} diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_D_ambgs.jasm b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_D_ambgs.jasm new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_D_ambgs.jasm @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2010, 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. + * + */ + +// A simple class to extend an abstract class and get loaded with different +// loaders. This class is loaded via Loader2. A similar named class will +// be loaded via the bootstrap loader. +// +// The following code is implemented as java assembler to avoid checks +// of javac. +// +// public class LE2_D_ambgs extends bug_21227 { +// +// LE2_D_ambgs() { +// _ref_to_be_p0wned = LE2_C._p0wnee; +// System.out.println("Gonna hack this thing"); +// } +// +// public LE2_D_ambgs[] make(LE2_A iface) { +// throw new Error("do not call me"); +// } +// } + +class LE2_D_ambgs extends LE2_C { + + Method LE2_D_ambgs:"()V" + stack 2 locals 1 + { + aload_0; + invokespecial Method LE2_C."":"()V"; + getstatic Field java/lang/System.out:"Ljava/io/PrintStream;"; + ldc String "Gonna hack this thing"; + invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V"; + return; + } + + public Method make:"(LLE2_A;)[LLE2_D_ambgs;" + stack 3 locals 2 + { + new class java/lang/Error; + dup; + ldc String "do not call me"; + invokespecial Method java/lang/Error."":"(Ljava/lang/String;)V"; + athrow; + } +} diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_D_ambgs.java b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_D_ambgs.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_D_ambgs.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2010, 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. + * + */ + +// A simple class to extend an abstract class and get loaded with different +// loaders. This class is loaded via the bootstrap loader. A similar named class will +// be loaded via Loader2. +public class LE2_D_ambgs extends LE2_C { + + // We are loaded by the bootstrap loader. iface is an object of a class + // loaded by Loader2. As it references LE2_D_ambgs, Loader2 will trigger + // loading the version known to it, which differs from this one. + public LE2_D_ambgs[] make(LE2_A iface) { + // This function needs to return a value known to be loaded from Loader2. + // Since I need to use a yet different loader, I need to make an unknown + // foreign call. In this case I'll be using an interface to make the + // unknown call, with but a single implementor so the compiler can do the + // upcast statically. + return iface == null ? null : iface.gen(); + } +} diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_Loader.java b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_Loader.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LE2_Loader.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2010, 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.io.File; +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; + +public class LE2_Loader extends ClassLoader { + + private static String classPath; + static { + classPath = System.getProperty("test.classes"); + if (classPath == null) { + classPath = ""; + } else { + classPath = classPath + File.separator; + } + } + + // Print routine to generate indented output that + // reflects recursive actions of the classloader. + int _recur = 0; + public void print(String msg) { + for (int i = 0; i < _recur; i++) { + System.out.print(" "); + } + System.out.println(">>LE2_Loader>> " + msg); + } + + protected Class findClass2(String name) throws ClassNotFoundException { + print("Fetching the implementation of " + name); + int old = _recur; + String filename = classPath + name.replace(".", "/") + ".impl2"; + try { + + FileInputStream fi = new FileInputStream(filename); + byte result[] = new byte[fi.available()]; + fi.read(result); + + print("call defineClass() on " + name); + _recur++; + // This will throw the expected LinkageError when loading the + // second version of class B. + Class clazz = defineClass(name, result, 0, result.length); + _recur = old; + print("Returning newly loaded class."); + return clazz; + } catch (Exception e) { + _recur = old; + print("Not found on disk: " + filename); + // If we caught an exception, either the class was not found or + // it was unreadable by our process. + return null; + } + } + + protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + // Attempt a disk load first. + Class c = findClass2(name); + if (c == null) { + // Check if the class has already been loaded. + print("Checking for prior loaded class " + name); + c = findLoadedClass(name); + print("Letting super-loader load " + name); + int old = _recur; + _recur++; + c = super.loadClass(name, false); + _recur = old; + } + if (resolve) { print("Resolving class " + name); resolveClass(c); } + print("Returning class " + c.getClassLoader() + ":" + name); + return c; + } +} diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LinkageErrorTest.java b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LinkageErrorTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/LinkageErrorTest.java @@ -0,0 +1,207 @@ +/* + * 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 exception messages of LinkageError. + * @compile LE2_D_ambgs.jasm + * @run shell myMove.sh LE2_D_ambgs.class LE2_D_ambgs.impl2 + * @compile LinkageErrorTest.java LE1.java LE2_B.java + * @run shell myMove.sh LE2_B.class LE2_B.impl2 + * @run main/othervm -Xbootclasspath/a:. LinkageErrorTest + */ + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class LinkageErrorTest { + + static private class LE1_Loader extends ClassLoader { + public LE1_Loader() { + super(); + } + + public LE1_Loader(String name) { + super(name, ClassLoader.getSystemClassLoader()); + } + + public Class myDefineClass(String name, byte[] b, int off, int len) throws ClassFormatError { + return defineClass(name, b, off, len, null); + } + } + + private static byte[] le1_getBytecodes(String myName) throws Exception { + InputStream is = LinkageErrorTest.class.getResourceAsStream(myName + ".class"); + if (is == null) { + System.out.println("is is null. File " + myName + ".class not found."); + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buf = new byte[4096]; + int len; + while ((len = is.read(buf)) != -1) { baos.write(buf, 0, len); } + buf = baos.toByteArray(); + System.out.println("sizeof(" + myName + ".class) == " + buf.length); + return buf; + } + + private static String expectedErrorMessageDuplicate1 = + "loader LinkageErrorTest$LE1_Loader (parent: \"app\" jdk.internal.loader.ClassLoaders$AppClassLoader) " + + "attempted duplicate class definition for test.LE1."; + + // Load the same class twice. + // This test is derived from runtime/Metaspace/DefineClass.java. + public static void test1_duplicate1() throws Exception { + String myName = "test.LE1"; + byte[] buf = le1_getBytecodes(myName.replace('.', '/')); + LE1_Loader cl = new LE1_Loader(); + + try { + @SuppressWarnings("unchecked") + Class dc = (Class) cl.myDefineClass(myName, buf, 0, buf.length); + System.out.println(dc); + // Should throw exception in SystemDictionary::check_constraints(), check for duplicates. + dc = (Class) cl.myDefineClass(myName, buf, 0, buf.length); + throw new RuntimeException("Expected LinkageError was not thrown."); + } catch (NoClassDefFoundError exc) { + throw new RuntimeException("Unexpected NoClassDefFoundError: " + exc); + } catch (LinkageError jle) { + // Second definition should fail. Check error message. + String errorMsg = jle.getMessage(); + if (!errorMsg.equals(expectedErrorMessageDuplicate1)) { + System.out.println("Expected: " + expectedErrorMessageDuplicate1 + "\n" + + "but got: " + errorMsg); + throw new RuntimeException("Wrong error message of LinkageError."); + } else { + System.out.println("Passed with message: " + errorMsg); + } + } + } + + private static String expectedErrorMessageDuplicate2 = + "loader \"LoaderForTestLE1_2\" (instance of LinkageErrorTest$LE1_Loader, " + + "parent: \"app\" jdk.internal.loader.ClassLoaders$AppClassLoader) " + + "attempted duplicate class definition for test.LE1."; + + // Load the same class twice. Use classloader with explicit name. + // This test is derived from runtime/Metaspace/DefineClass.java. + public static void test1_duplicate2() throws Exception { + String myName = "test.LE1"; + byte[] buf = le1_getBytecodes(myName.replace('.', '/')); + LE1_Loader cl = new LE1_Loader("LoaderForTestLE1_2"); + + try { + @SuppressWarnings("unchecked") + Class dc = (Class) cl.myDefineClass(myName, buf, 0, buf.length); + System.out.println(dc); + // Should throw exception in SystemDictionary::check_constraints(), check for duplicates. + dc = (Class) cl.myDefineClass(myName, buf, 0, buf.length); + throw new RuntimeException("Expected LinkageError was not thrown."); + } catch (NoClassDefFoundError exc) { + throw new RuntimeException("Unexpected NoClassDefFoundError: " + exc); + } catch (LinkageError jle) { + // Second definition should fail. Check error message. + String errorMsg = jle.getMessage(); + if (!errorMsg.equals(expectedErrorMessageDuplicate2)) { + System.out.println("Expected: " + expectedErrorMessageDuplicate2 + "\n" + + "but got: " + errorMsg); + throw new RuntimeException("Wrong error message of LinkageError."); + } else { + System.out.println("Passed with message: " + errorMsg); + } + } + } + + // Force LinkageError. + // + // Derived from test runtime/6626217. + // + // Uses the specialized class loader LE2_Loader. + // LE2_Loader first looks for a file with ending ".impl2" to load. If + // it does not find it, it delegates to the super class loader. + // + // A // interface + // | + // B // Compiled to B.impl2 so that it is found by our special class loader. + // // B uses D, so that loading B triggers loading D. + // + // C // An abstract class. + // | + // D // Class with two different implementations D1 and D2. D2 is + // // compiled to D.impl2 so that it is found by our special class loader. + // + // First, the bootstrap loader will load D1. It already has loaded interface A. + // Then, the second class loader LE2_Loader will load B. Recursive, it tries to + // load interface A. As it does not find it (there is no A.impl2), it asks the + // bootstrap classloader for A. + // Then it loads the D2 variant of D from D.impl2 and it's superclass C. This + // fails as D1 is already loaded with the same superclass. + + static String expectedErrorMessage2 = + "loader constraint violation: loader LE2_Loader " + + "(parent: \"app\" jdk.internal.loader.ClassLoaders$AppClassLoader) wants to load " + + "class LE2_D_ambgs. A different class with the same name was previously loaded " + + "by \"app\" (instance of jdk.internal.loader.ClassLoaders$AppClassLoader, " + + "parent: \"platform\" jdk.internal.loader.ClassLoaders$PlatformClassLoader)."; + + public static void test2_access() throws Exception { + try { + // Make a Class 'LE2_D_ambgs' under the default loader. + // This uses the implementation from the .java file. + LE2_C c_1 = new LE2_D_ambgs(); + + // Some classes under a new Loader, loader2, including, indirectly, + // another version of 'LE2_D_ambgs' + ClassLoader loader2 = new LE2_Loader(); + Class class2 = loader2.loadClass("LE2_B"); + LE2_A iface = (LE2_A)class2.newInstance(); + + // Call D1.make() loaded by bootstrap loader with B loaded by Loader2. + LE2_D_ambgs[] x2 = c_1.make(iface); + + throw new RuntimeException("Expected LinkageError was not thrown."); + } catch (LinkageError jle) { + String errorMsg = jle.getMessage(); + if (!errorMsg.equals(expectedErrorMessage2)) { + System.out.println("Expected: " + expectedErrorMessage2 + "\n" + + "but got: " + errorMsg); + throw new RuntimeException("Wrong error message of LinkageError."); + } else { + System.out.println("Passed with message: " + errorMsg); + } + } + } + + public static void main(String[] args) throws Exception { + test1_duplicate1(); + test1_duplicate2(); + test2_access(); + } +} + + diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/myMove.sh b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/myMove.sh new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/LinkageError/myMove.sh @@ -0,0 +1,32 @@ +# +# 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. +# + +# A script just to call 'mv' platform independent and from the +# @run tag of the test. It operates on the directory containing +# the compiled class files of the test. + +# Adding common setup Variables for running shell tests. +. ${TESTSRC}/../../../test_env.sh + +echo "Moving files: ${MV} ${TESTCLASSES}${FS}$1 ${TESTCLASSES}${FS}$2" +${MV} ${TESTCLASSES}${FS}$1 ${TESTCLASSES}${FS}$2