--- old/src/hotspot/share/classfile/defaultMethods.cpp 2017-12-04 09:20:26.244895978 -0500 +++ new/src/hotspot/share/classfile/defaultMethods.cpp 2017-12-04 09:20:26.061373577 -0500 @@ -26,6 +26,7 @@ #include "classfile/bytecodeAssembler.hpp" #include "classfile/defaultMethods.hpp" #include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" #include "memory/allocation.hpp" @@ -626,7 +627,7 @@ while (super != NULL) { for (int i = 0; i < super->methods()->length(); ++i) { Method* m = super->methods()->at(i); - if (m->is_overpass() || m->is_static()) { + if (m->is_overpass() || m->is_static()) { // m is a method that would have been a miranda if not for the // default method processing that occurred on behalf of our superclass, // so it's a method we want to re-examine in this new context. That is, @@ -683,10 +684,11 @@ Symbol* _method_name; Symbol* _method_signature; StatefulMethodFamily* _family; + bool _cur_class_is_interface; public: - FindMethodsByErasedSig(Symbol* name, Symbol* signature) : - _method_name(name), _method_signature(signature), + FindMethodsByErasedSig(Symbol* name, Symbol* signature, bool is_interf) : + _method_name(name), _method_signature(signature), _cur_class_is_interface(is_interf), _family(NULL) {} void get_discovered_family(MethodFamily** family) { @@ -709,14 +711,17 @@ InstanceKlass* iklass = current_class(); Method* m = iklass->find_method(_method_name, _method_signature); - // private interface methods are not candidates for default methods - // invokespecial to private interface methods doesn't use default method logic - // private class methods are not candidates for default methods, - // private methods do not override default methods, so need to perform - // default method inheritance without including private methods - // The overpasses are your supertypes' errors, we do not include them - // future: take access controls into account for superclass methods - if (m != NULL && !m->is_static() && !m->is_overpass() && !m->is_private()) { + // Private interface methods are not candidates for default methods. + // invokespecial to private interface methods doesn't use default method logic. + // Private class methods are not candidates for default methods. + // Private methods do not override default methods, so need to perform + // default method inheritance without including private methods. + // The overpasses are your supertypes' errors, we do not include them. + // Non-public methods in java.lang.Object are not candidates for default + // methods. + // Future: take access controls into account for superclass methods + if (m != NULL && !m->is_static() && !m->is_overpass() && !m->is_private() && + (!_cur_class_is_interface || !SystemDictionary::is_nonpublic_Object_method(m))) { if (_family == NULL) { _family = new StatefulMethodFamily(); } @@ -726,8 +731,8 @@ scope->add_mark(restorer); } else { // This is the rule that methods in classes "win" (bad word) over - // methods in interfaces. This works because of single inheritance - // private methods in classes do not "win", they will be found + // methods in interfaces. This works because of single inheritance. + // Private methods in classes do not "win", they will be found // first on searching, but overriding for invokevirtual needs // to find default method candidates for the same signature _family->set_target_if_empty(m); @@ -745,10 +750,10 @@ static void generate_erased_defaults( InstanceKlass* klass, GrowableArray* empty_slots, - EmptyVtableSlot* slot, TRAPS) { + EmptyVtableSlot* slot, bool is_intf, TRAPS) { // sets up a set of methods with the same exact erased signature - FindMethodsByErasedSig visitor(slot->name(), slot->signature()); + FindMethodsByErasedSig visitor(slot->name(), slot->signature(), is_intf); visitor.run(klass); MethodFamily* family; @@ -817,7 +822,7 @@ slot->print_on(&ls); ls.cr(); } - generate_erased_defaults(klass, empty_slots, slot, CHECK); + generate_erased_defaults(klass, empty_slots, slot, klass->is_interface(), CHECK); } log_debug(defaultmethods)("Creating defaults and overpasses..."); create_defaults_and_exceptions(empty_slots, klass, CHECK); --- old/src/hotspot/share/classfile/systemDictionary.hpp 2017-12-04 09:20:26.751526673 -0500 +++ new/src/hotspot/share/classfile/systemDictionary.hpp 2017-12-04 09:20:26.567731969 -0500 @@ -649,6 +649,12 @@ static bool is_platform_class_loader(oop class_loader); static void clear_invoke_method_table(); + // Returns TRUE if the method is a non-public member of class java.lang.Object. + static bool is_nonpublic_Object_method(Method* m) { + assert(m != NULL, "Unexpected NULL Method*"); + return !m->is_public() && m->method_holder() == SystemDictionary::Object_klass(); + } + protected: static InstanceKlass* find_shared_class(Symbol* class_name); --- old/src/hotspot/share/oops/klassVtable.cpp 2017-12-04 09:20:27.259695060 -0500 +++ new/src/hotspot/share/oops/klassVtable.cpp 2017-12-04 09:20:27.072895171 -0500 @@ -454,8 +454,11 @@ } else { super_method = method_at(i); } - // Check if method name matches - if (super_method->name() == name && super_method->signature() == signature) { + // Check if method name matches. Ignore match if klass is an interface and the + // matching method is a non-public java.lang.Object method. (See JVMS 5.4.3.4) + if (super_method->name() == name && super_method->signature() == signature && + (!_klass->is_interface() || + !SystemDictionary::is_nonpublic_Object_method(super_method))) { // get super_klass for method_holder for the found method InstanceKlass* super_klass = super_method->method_holder(); @@ -800,8 +803,12 @@ for (const Klass* cursuper = super; cursuper != NULL; cursuper = cursuper->super()) { - if (InstanceKlass::cast(cursuper)->find_local_method(name, signature, - Klass::find_overpass, Klass::skip_static, Klass::skip_private) != NULL) { + Method* found_mth = InstanceKlass::cast(cursuper)->find_local_method(name, signature, + Klass::find_overpass, Klass::skip_static, Klass::skip_private); + // Continue looking if found_mth is a non-public method in java.lang.Object + // because such methods are skipped over during interface method resolution + // and may 'mask' an actual miranda method. + if (found_mth != NULL && !SystemDictionary::is_nonpublic_Object_method(found_mth)) { return false; } } --- /dev/null 2017-10-28 21:13:49.622000222 -0400 +++ new/test/hotspot/jtreg/runtime/clone/DefaultClone.jasm 2017-12-04 09:20:27.611040645 -0500 @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2017, 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 8154587 + * @summary Check that a default method named clone() does not get masked by + * java.lang.Object.clone(). + * @compile DefaultClone.jasm + * @run main DefaultClone + */ + +// The below .jasm code implements the following java code: +// +// public class DefaultClone { +// +// interface I1 { +// default Object clone() { +// return "In I1's clone()"; +// } +// } +// +// +// interface I2 extends I1 { } +// +// +// static class C implements I2 { +// public Object clone() { +// return "In C's clone()"; +// } +// } +// +// +// static Object test(I2 i) { return i.clone(); } +// +// public static void main(String[] args) { +// String s = (String)test(new C()); +// if (!s.equals("In C's clone()")) { +// throw new RuntimeException("Wrong clone() called"); +// } +// } +// } + + +interface DefaultClone$I1 version 53:0 { + + public Method clone:"()Ljava/lang/Object;" stack 1 locals 1 { + ldc String "In I1\'s clone()"; + areturn; + } + + static abstract interface InnerClass I1=class DefaultClone$I1 of class DefaultClone; + +} // end Class DefaultClone$I1 + + + +interface DefaultClone$I2 implements DefaultClone$I1 version 53:0 { + + static abstract interface InnerClass I2=class DefaultClone$I2 of class DefaultClone; + static abstract interface InnerClass I1=class DefaultClone$I1 of class DefaultClone; + +} // end Class DefaultClone$I2 + + +super class DefaultClone$C implements DefaultClone$I2 version 53:0 { + + Method "":"()V" stack 1 locals 1 { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } + + public Method clone:"()Ljava/lang/Object;" stack 1 locals 1 { + ldc String "In C\'s clone()"; + areturn; + } + + static InnerClass C=class DefaultClone$C of class DefaultClone; + static abstract interface InnerClass I2=class DefaultClone$I2 of class DefaultClone; + +} // end Class DefaultClone$C + + +super public class DefaultClone version 53:0 { + + public Method "":"()V" stack 1 locals 1 { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } + + static Method test:"(LDefaultClone$I2;)Ljava/lang/Object;" stack 1 locals 1 { + aload_0; + invokeinterface InterfaceMethod DefaultClone$I2.clone:"()Ljava/lang/Object;", 1; + areturn; + } + + public static Method main:"([Ljava/lang/String;)V" stack 3 locals 2 { + new class DefaultClone$C; + dup; + invokespecial Method DefaultClone$C."":"()V"; + invokestatic Method test:"(LDefaultClone$I2;)Ljava/lang/Object;"; + checkcast class java/lang/String; + astore_1; + aload_1; + ldc String "In C\'s clone()"; + invokevirtual Method java/lang/String.equals:"(Ljava/lang/Object;)Z"; + ifne L33; + new class java/lang/RuntimeException; + dup; + ldc String "Wrong clone() called"; + invokespecial Method java/lang/RuntimeException."":"(Ljava/lang/String;)V"; + athrow; + L33: stack_frame_type append; + locals_map class java/lang/String; + return; + } + + static InnerClass C=class DefaultClone$C of class DefaultClone; + static abstract interface InnerClass I2=class DefaultClone$I2 of class DefaultClone; + static abstract interface InnerClass I1=class DefaultClone$I1 of class DefaultClone; + +} // end Class DefaultClone