# HG changeset patch # User goetz # Date 1521540963 -3600 # Tue Mar 20 11:16:03 2018 +0100 # Node ID ee33ed1efb3d05a3fe4983d8705f0a16556f0465 # Parent a1617a75c231b1ec7fcdc37a36b96e5cad2ed643 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 @@ -4106,6 +4106,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("instance of %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 @@ -2143,10 +2143,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); @@ -2155,16 +2160,16 @@ 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 "; } else { return; } @@ -2177,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()); + } } } } @@ -2188,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, @@ -3094,7 +3106,7 @@ NOT_PRODUCT(SystemDictionary::verify()); } -// caller needs ResourceMark +// Caller needs ResourceMark. const char* SystemDictionary::loader_name(const oop loader) { return ((loader) == NULL ? "" : InstanceKlass::cast((loader)->klass())->name()->as_C_string()); 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 @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "jvm.h" +#include "classfile/javaClasses.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" #include "interpreter/linkResolver.hpp" @@ -505,21 +506,21 @@ 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(); - char* failed_type_name = failed_type_symbol->as_C_string(); - size_t buflen = strlen(msg) + strlen(curr_class) + strlen(sig) + + const char* curr_class = klass->external_name(); + const char* method = 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(); + const char* failed_type_name = failed_type_symbol->as_klass_external_name(); + size_t buflen = strlen(msg) + strlen(curr_class) + strlen(method) + strlen(loader1) + strlen(sel_class) + strlen(loader2) + strlen(super_class) + strlen(failed_type_name); char* buf = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, buflen); - jio_snprintf(buf, buflen, msg, curr_class, sig, loader1, sel_class, loader2, + jio_snprintf(buf, buflen, msg, curr_class, method, loader1, sel_class, loader2, super_class, failed_type_name); THROW_MSG_(vmSymbols::java_lang_LinkageError(), buf, false); } @@ -1235,17 +1236,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_klass_external_name(); 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/LoaderConstraints/common/C.jasm b/test/hotspot/jtreg/runtime/LoaderConstraints/common/C.jasm --- a/test/hotspot/jtreg/runtime/LoaderConstraints/common/C.jasm +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/common/C.jasm @@ -26,6 +26,8 @@ // class C inherits unrelated defaults for m() from types I and J // C is not abstract and does not override abstract method m() in I +package test; + super public class C implements I, J version 52:0 { public Method "":"()V" stack 1 locals 1 { diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/common/Foo.java b/test/hotspot/jtreg/runtime/LoaderConstraints/common/Foo.java --- a/test/hotspot/jtreg/runtime/LoaderConstraints/common/Foo.java +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/common/Foo.java @@ -21,4 +21,6 @@ * questions. */ +package test; + public class Foo {} diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/common/J.java b/test/hotspot/jtreg/runtime/LoaderConstraints/common/J.java --- a/test/hotspot/jtreg/runtime/LoaderConstraints/common/J.java +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/common/J.java @@ -21,6 +21,8 @@ * questions. */ +package test; + public interface J { public default Foo m() { return null; } } diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/common/PreemptingClassLoader.java b/test/hotspot/jtreg/runtime/LoaderConstraints/common/PreemptingClassLoader.java --- a/test/hotspot/jtreg/runtime/LoaderConstraints/common/PreemptingClassLoader.java +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/common/PreemptingClassLoader.java @@ -27,14 +27,26 @@ public class PreemptingClassLoader extends ClassLoader { private final Set names = new HashSet<>(); + boolean checkLoaded = true; public PreemptingClassLoader(String... names) { for (String n : names) this.names.add(n); } + public PreemptingClassLoader(String name, String[] names) { + super(name, ClassLoader.getSystemClassLoader()); + for (String n : names) this.names.add(n); + } + + public PreemptingClassLoader(String name, String[] names, boolean cL) { + super(name, ClassLoader.getSystemClassLoader()); + 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 = findLoadedClass(name); + Class result = checkLoaded ? findLoadedClass(name) : null; if (result == null) { String filename = name.replace('.', '/') + ".class"; try (InputStream data = getResourceAsStream(filename)) { diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/differentLE/A.java b/test/hotspot/jtreg/runtime/LoaderConstraints/differentLE/A.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/differentLE/A.java @@ -0,0 +1,30 @@ +/* + * 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 simple interface, to allow an unknown foreign call from a class +// loaded with the bootstrap loader to a class loaded with Loader2. +public interface A { + public D_ambgs[] gen(); +} + diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/differentLE/B.java b/test/hotspot/jtreg/runtime/LoaderConstraints/differentLE/B.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/differentLE/B.java @@ -0,0 +1,33 @@ +/* + * 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. + * + */ + +// This class is loaded via Loader2. Using D_ambgs here will trigger +// loading it's second version with Loader2. +public class B implements A { + public D_ambgs[] gen() { + D_ambgs[] x = new D_ambgs[1]; + x[0] = new D_ambgs(); + return x; + } +} diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/differentLE/C.java b/test/hotspot/jtreg/runtime/LoaderConstraints/differentLE/C.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/differentLE/C.java @@ -0,0 +1,27 @@ +/* + * 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. + * + */ + +abstract public class C { + public abstract D_ambgs[] make(A iface); // abstract factory +} diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/differentLE/D_ambgs.jasm b/test/hotspot/jtreg/runtime/LoaderConstraints/differentLE/D_ambgs.jasm new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/differentLE/D_ambgs.jasm @@ -0,0 +1,65 @@ +/* + * 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 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 D_ambgs extends bug_21227 { +// +// D_ambgs() { +// System.out.println("Gonna hack this thing"); +// } +// +// public D_ambgs[] make(A iface) { +// throw new Error("do not call me"); +// } +// } + +class D_ambgs extends C { + + Method D_ambgs:"()V" + stack 2 locals 1 + { + aload_0; + invokespecial Method 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:"(LA;)[LD_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/LoaderConstraints/differentLE/D_ambgs.java b/test/hotspot/jtreg/runtime/LoaderConstraints/differentLE/D_ambgs.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/differentLE/D_ambgs.java @@ -0,0 +1,41 @@ +/* + * 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 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 D_ambgs extends C { + + // We are loaded by the bootstrap loader. iface is an object of a class + // loaded by Loader2. As it references D_ambgs, Loader2 will trigger + // loading the version known to it, which differs from this one. + public D_ambgs[] make(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/LoaderConstraints/differentLE/Test.java b/test/hotspot/jtreg/runtime/LoaderConstraints/differentLE/Test.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/differentLE/Test.java @@ -0,0 +1,114 @@ +/* + * 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. + * @library /test/lib + * @compile D_ambgs.jasm + * @run driver ClassFileInstaller D_ambgs + * @compile ../common/PreemptingClassLoader.java + * D_ambgs.java Test.java B.java + * @run driver ClassFileInstaller B + * @run main/othervm Test + */ + +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 Test { + + // Force LinkageError. + // + // Derived from test runtime/6626217. + // + // Uses the specialized class loader PreemptingClassLoader. + // PreemptingClassLoader only loads files with names passed to it in its + // constructor. If it does not find it, it delegates to the super class loader. + // + // A // interface + // | + // B // Compiled to the current working directory 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 the current working directory 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 PreemptingClassLoader will load B. Recursive, + // it tries to load interface A. As it does not find it (there is no A.impl2), + // it asks the super classloader for A. + // Then it loads the D2 variant of D from the current working directory and it's + // superclass C. This fails as D1 is already loaded with the same superclass. + + static String expectedErrorMessage = + "loader constraint violation: loader instance of PreemptingClassLoader " + + "(parent: \"app\" jdk.internal.loader.ClassLoaders$AppClassLoader) wants to load " + + "class 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 test_access() throws Exception { + try { + // Make a Class 'D_ambgs' under the default loader. + // This uses the implementation from the .java file. + C c_1 = new D_ambgs(); + + // Some classes under a new Loader, loader2, including, indirectly, + // another version of 'D_ambgs' + String[] classNames = {"B", "D_ambgs"}; + + ClassLoader loader2 = new PreemptingClassLoader(null, classNames, false); + Class class2 = loader2.loadClass("B"); + A iface = (A)class2.newInstance(); + + // Call D1.make() loaded by bootstrap loader with B loaded by Loader2. + 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(expectedErrorMessage)) { + System.out.println("Expected: " + expectedErrorMessage + "\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 { + test_access(); + } +} + diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/duplicateLE/Test.java b/test/hotspot/jtreg/runtime/LoaderConstraints/duplicateLE/Test.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/duplicateLE/Test.java @@ -0,0 +1,98 @@ +/* + * 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 + * @compile ../common/Foo.java + * ../common/PreemptingClassLoader.java + * @run main/othervm Test + * @run main/othervm -Xint Test + * @run main/othervm -Xcomp Test + * @run main/othervm -XX:+TieredCompilation -XX:TieredStopAtLevel=1 Test + * @run main/othervm -XX:-TieredCompilation Test + */ + +public class Test { + + // Check that all names have external formatting ('.' and not '/' in package names). + // Check for parent of class loader. + static String expectedErrorMessage1 = + "loader instance of PreemptingClassLoader (parent: \"app\" " + + "jdk.internal.loader.ClassLoaders$AppClassLoader) attempted duplicate class definition for test.Foo."; + + // 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 test1() throws Exception { + String[] classNames = {"test.Foo"}; + ClassLoader l = new PreemptingClassLoader(null, classNames, false); + l.loadClass("test.Foo"); + try { + l.loadClass("test.Foo").newInstance(); + throw new RuntimeException("Expected LinkageError exception not thrown"); + } catch (LinkageError e) { + String errorMsg = e.getMessage(); + if (!errorMsg.equals(expectedErrorMessage1)) { + System.out.println("Expected: " + expectedErrorMessage1 + "\n" + + "but got: " + errorMsg); + throw new RuntimeException("Wrong LinkageError exception thrown: " + errorMsg); + } + System.out.println("Passed with message: " + errorMsg); + } + } + + // Check that all names have external formatting ('.' and not '/' in package names). + // Check for name and parent of class loader. + static String expectedErrorMessage2 = + "loader \"DuplicateLE_Test_Loader\" (instance of PreemptingClassLoader, parent: \"app\" " + + "jdk.internal.loader.ClassLoaders$AppClassLoader) attempted duplicate class definition for test.Foo."; + + // Same as test1, but ClassLoader has a name. + public static void test2() throws Exception { + String[] classNames = {"test.Foo"}; + ClassLoader l = new PreemptingClassLoader("DuplicateLE_Test_Loader", classNames, false); + l.loadClass("test.Foo"); + try { + l.loadClass("test.Foo"); + throw new RuntimeException("Expected LinkageError exception not thrown"); + } catch (LinkageError e) { + String errorMsg = e.getMessage(); + if (!errorMsg.equals(expectedErrorMessage2)) { + System.out.println("Expected: " + expectedErrorMessage2 + "\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 { + test1(); + test2(); + } +} + diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/itableICCE/I.java b/test/hotspot/jtreg/runtime/LoaderConstraints/itableICCE/I.java --- a/test/hotspot/jtreg/runtime/LoaderConstraints/itableICCE/I.java +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/itableICCE/I.java @@ -21,6 +21,8 @@ * questions. */ +package test; + public interface I { public default Foo m() { return null; } } diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/itableICCE/Task.java b/test/hotspot/jtreg/runtime/LoaderConstraints/itableICCE/Task.java --- a/test/hotspot/jtreg/runtime/LoaderConstraints/itableICCE/Task.java +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/itableICCE/Task.java @@ -21,6 +21,8 @@ * questions. */ +package test; + public class Task implements Runnable { public void run() { diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/itableICCE/Test.java b/test/hotspot/jtreg/runtime/LoaderConstraints/itableICCE/Test.java --- a/test/hotspot/jtreg/runtime/LoaderConstraints/itableICCE/Test.java +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/itableICCE/Test.java @@ -44,14 +44,14 @@ // ICCE). So, no LinkageError exception should be thrown because the loader // constraint check that would cause the LinkageError should not be done. public static void main(String... args) throws Exception { - Class c = Foo.class; // forces standard class loader to load Foo - ClassLoader l = new PreemptingClassLoader("Task", "Foo", "C", "I"); - Runnable r = (Runnable) l.loadClass("Task").newInstance(); + Class c = test.Foo.class; // forces standard class loader to load Foo + ClassLoader l = new PreemptingClassLoader("test.Task", "test.Foo", "test.C", "test.I"); + Runnable r = (Runnable) l.loadClass("test.Task").newInstance(); try { r.run(); // Cause an ICCE because both I and J define m()LFoo; throw new RuntimeException("Expected ICCE exception not thrown"); } catch (IncompatibleClassChangeError e) { - if (!e.getMessage().contains("Conflicting default methods: I.m J.m")) { + if (!e.getMessage().contains("Conflicting default methods: test/I.m test/J.m")) { throw new RuntimeException("Wrong ICCE exception thrown: " + e.getMessage()); } } diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/itableLdrConstraint/I.java b/test/hotspot/jtreg/runtime/LoaderConstraints/itableLdrConstraint/I.java --- a/test/hotspot/jtreg/runtime/LoaderConstraints/itableLdrConstraint/I.java +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/itableLdrConstraint/I.java @@ -21,6 +21,8 @@ * questions. */ +package test; + public interface I { public Foo m(); } diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/itableLdrConstraint/Task.java b/test/hotspot/jtreg/runtime/LoaderConstraints/itableLdrConstraint/Task.java --- a/test/hotspot/jtreg/runtime/LoaderConstraints/itableLdrConstraint/Task.java +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/itableLdrConstraint/Task.java @@ -21,6 +21,8 @@ * questions. */ +package test; + public class Task implements Runnable { public void run() { diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/itableLdrConstraint/Test.java b/test/hotspot/jtreg/runtime/LoaderConstraints/itableLdrConstraint/Test.java --- a/test/hotspot/jtreg/runtime/LoaderConstraints/itableLdrConstraint/Test.java +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/itableLdrConstraint/Test.java @@ -35,6 +35,15 @@ public class Test { + static String expectedErrorMessage1 = + "loader constraint violation in interface itable initialization for class test.C: " + + "when selecting method test.I.m()Ltest/Foo; the class loader instance of PreemptingClassLoader " + + "(parent: \"app\" jdk.internal.loader.ClassLoaders$AppClassLoader) " + + "for super interface test.I, and the class loader \"app\" " + + "(instance of jdk.internal.loader.ClassLoaders$AppClassLoader, " + + "parent: \"platform\" jdk.internal.loader.ClassLoaders$PlatformClassLoader) " + + "of the selected method's type, test.J have different Class objects for the type test.Foo used in the signature"; + // Test that the error message is correct when a loader constraint error is // detected during itable creation. // @@ -43,18 +52,56 @@ // type super interface J. The selected method is not an overpass method nor // otherwise excluded from loader constraint checking. So, a LinkageError // exception should be thrown because the loader constraint check will fail. - public static void main(String... args) throws Exception { - Class c = Foo.class; // forces standard class loader to load Foo - ClassLoader l = new PreemptingClassLoader("Task", "Foo", "C", "I"); - Runnable r = (Runnable) l.loadClass("Task").newInstance(); + public static void test1() throws Exception { + Class c = test.Foo.class; // forces standard class loader to load Foo + ClassLoader l = new PreemptingClassLoader("test.Task", "test.Foo", "test.C", "test.I"); + Runnable r = (Runnable) l.loadClass("test.Task").newInstance(); try { r.run(); throw new RuntimeException("Expected LinkageError exception not thrown"); } catch (LinkageError e) { - if (!e.getMessage().contains( - "loader constraint violation in interface itable initialization for class C:")) { - throw new RuntimeException("Wrong LinkageError exception thrown: " + e.getMessage()); + String errorMsg = e.getMessage(); + if (!errorMsg.equals(expectedErrorMessage1)) { + System.out.println("Expected: " + expectedErrorMessage1 + "\n" + + "but got: " + errorMsg); + throw new RuntimeException("Wrong LinkageError exception thrown: " + errorMsg); } + System.out.println("Passed with message: " + errorMsg); } } + + static String expectedErrorMessage2 = + "loader constraint violation in interface itable initialization for class test.C: " + + "when selecting method test.I.m()Ltest/Foo; the class loader \"ItableLdrCnstrnt_Test_Loader\" " + + "(instance of PreemptingClassLoader, parent: \"app\" " + + "jdk.internal.loader.ClassLoaders$AppClassLoader) for super interface test.I, " + + "and the class loader \"app\" (instance of jdk.internal.loader.ClassLoaders$AppClassLoader, " + + "parent: \"platform\" jdk.internal.loader.ClassLoaders$PlatformClassLoader) " + + "of the selected method's type, test.J have different Class objects for the type test.Foo used in the signature"; + + // Same as test1, but ClassLoader has a name. + public static void test2() throws Exception { + Class c = test.Foo.class; // Forces standard class loader to load Foo. + String[] classNames = {"test.Task", "test.Foo", "test.C", "test.I"}; + ClassLoader l = new PreemptingClassLoader("ItableLdrCnstrnt_Test_Loader", classNames); + Runnable r = (Runnable) l.loadClass("test.Task").newInstance(); + try { + r.run(); + throw new RuntimeException("Expected LinkageError exception not thrown"); + } catch (LinkageError e) { + String errorMsg = e.getMessage(); + if (!errorMsg.equals(expectedErrorMessage2)) { + System.out.println("Expected: " + expectedErrorMessage2 + "\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 { + test1(); + test2(); + } + } diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/vtableAME/I.java b/test/hotspot/jtreg/runtime/LoaderConstraints/vtableAME/I.java --- a/test/hotspot/jtreg/runtime/LoaderConstraints/vtableAME/I.java +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/vtableAME/I.java @@ -21,6 +21,8 @@ * questions. */ +package test; + public interface I extends J { public Foo m(); } diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/vtableAME/Task.java b/test/hotspot/jtreg/runtime/LoaderConstraints/vtableAME/Task.java --- a/test/hotspot/jtreg/runtime/LoaderConstraints/vtableAME/Task.java +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/vtableAME/Task.java @@ -21,4 +21,6 @@ * questions. */ +package test; + public class Task extends C { } diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/vtableAME/Test.java b/test/hotspot/jtreg/runtime/LoaderConstraints/vtableAME/Test.java --- a/test/hotspot/jtreg/runtime/LoaderConstraints/vtableAME/Test.java +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/vtableAME/Test.java @@ -48,16 +48,16 @@ // because the loader constraint check that would cause the LinkageError // should not be done. public static void main(String args[]) throws Exception { - Class c = Foo.class; // forces standard class loader to load Foo - ClassLoader l = new PreemptingClassLoader("Task", "Foo", "I", "J"); - l.loadClass("Foo"); - l.loadClass("Task").newInstance(); - Task t = new Task(); + Class c = test.Foo.class; // forces standard class loader to load Foo + ClassLoader l = new PreemptingClassLoader("test.Task", "test.Foo", "test.I", "test.J"); + l.loadClass("test.Foo"); + l.loadClass("test.Task").newInstance(); + test.Task t = new test.Task(); try { t.m(); // Should get AME throw new RuntimeException("Missing AbstractMethodError exception"); } catch (AbstractMethodError e) { - if (!e.getMessage().contains("Method Task.m()LFoo; is abstract")) { + if (!e.getMessage().contains("Method test/Task.m()Ltest/Foo; is abstract")) { throw new RuntimeException("Wrong AME exception thrown: " + e.getMessage()); } } diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/vtableLdrConstraint/I.java b/test/hotspot/jtreg/runtime/LoaderConstraints/vtableLdrConstraint/I.java --- a/test/hotspot/jtreg/runtime/LoaderConstraints/vtableLdrConstraint/I.java +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/vtableLdrConstraint/I.java @@ -21,5 +21,7 @@ * questions. */ +package test; + public interface I extends J { } diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/vtableLdrConstraint/Task.java b/test/hotspot/jtreg/runtime/LoaderConstraints/vtableLdrConstraint/Task.java --- a/test/hotspot/jtreg/runtime/LoaderConstraints/vtableLdrConstraint/Task.java +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/vtableLdrConstraint/Task.java @@ -21,6 +21,8 @@ * questions. */ +package test; + public class Task extends C { public Foo m() { diff --git a/test/hotspot/jtreg/runtime/LoaderConstraints/vtableLdrConstraint/Test.java b/test/hotspot/jtreg/runtime/LoaderConstraints/vtableLdrConstraint/Test.java --- a/test/hotspot/jtreg/runtime/LoaderConstraints/vtableLdrConstraint/Test.java +++ b/test/hotspot/jtreg/runtime/LoaderConstraints/vtableLdrConstraint/Test.java @@ -35,6 +35,14 @@ public class Test { + static String expectedErrorMessage1 = + "loader constraint violation for class test.Task: when selecting overriding method test.Task.m()Ltest/Foo; " + + "the class loader instance of PreemptingClassLoader " + + "(parent: \"app\" jdk.internal.loader.ClassLoaders$AppClassLoader) of the selected method's type test.Task, " + + "and the class loader \"app\" (instance of jdk.internal.loader.ClassLoaders$AppClassLoader, " + + "parent: \"platform\" jdk.internal.loader.ClassLoaders$PlatformClassLoader) for its super type test.J " + + "have different Class objects for the type test.Foo used in the signature"; + // Test that the error message is correct when a loader constraint error is // detected during vtable creation. // @@ -42,22 +50,55 @@ // 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 main(String args[]) throws Exception { - Class c = Foo.class; // forces standard class loader to load Foo - ClassLoader l = new PreemptingClassLoader("Task", "Foo", "I"); - l.loadClass("Foo"); + public static void test1() throws Exception { + Class c = test.Foo.class; // forces standard class loader to load Foo + ClassLoader l = new PreemptingClassLoader("test.Task", "test.Foo", "test.I"); + l.loadClass("test.Foo"); try { - l.loadClass("Task").newInstance(); + l.loadClass("test.Task").newInstance(); throw new RuntimeException("Expected LinkageError exception not thrown"); } catch (LinkageError e) { - if (!e.getMessage().contains( - "loader constraint violation for class Task: when selecting overriding method") || - !e.getMessage().contains( - "for its super type J have different Class objects for the type Foo")) { - throw new RuntimeException("Wrong LinkageError exception thrown: " + e.getMessage()); + String errorMsg = e.getMessage(); + if (!errorMsg.equals(expectedErrorMessage1)) { + System.out.println("Expected: " + expectedErrorMessage1 + "\n" + + "but got: " + errorMsg); + throw new RuntimeException("Wrong LinkageError exception thrown: " + errorMsg); } + System.out.println("Passed with message: " + errorMsg); } } + static String expectedErrorMessage2 = + "loader constraint violation for class test.Task: when selecting overriding method test.Task.m()Ltest/Foo; " + + "the class loader \"VtableLdrCnstrnt_Test_Loader\" (instance of PreemptingClassLoader, " + + "parent: \"app\" jdk.internal.loader.ClassLoaders$AppClassLoader) of the selected method's type test.Task, " + + "and the class loader \"app\" (instance of jdk.internal.loader.ClassLoaders$AppClassLoader, " + + "parent: \"platform\" jdk.internal.loader.ClassLoaders$PlatformClassLoader) for its super type test.J " + + "have different Class objects for the type test.Foo used in the signature"; + + // Same as test1, but ClassLoader has a name. + public static void test2() throws Exception { + Class c = test.Foo.class; // Forces standard class loader to load Foo. + String[] classNames = {"test.Task", "test.Foo", "test.I"}; + ClassLoader l = new PreemptingClassLoader("VtableLdrCnstrnt_Test_Loader", classNames); + l.loadClass("test.Foo"); + try { + l.loadClass("test.Task").newInstance(); + throw new RuntimeException("Expected LinkageError exception not thrown"); + } catch (LinkageError e) { + String errorMsg = e.getMessage(); + if (!errorMsg.equals(expectedErrorMessage2)) { + System.out.println("Expected: " + expectedErrorMessage2 + "\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 { + test1(); + test2(); + } }