# HG changeset patch # User dholmes # Date 1475545701 14400 # Node ID 2091069b6851f39e0f17cbea4791c6cc28c53444 # Parent e9c6bbf513e5720afd923cc5b97598c802785b74 8081800: AbstractMethodError when evaluating a private method in an interface via debugger Reviewed-by: acorn, dcubed, coleenp diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/make/test/JtregNative.gmk --- a/hotspot/make/test/JtregNative.gmk Wed Sep 28 18:40:50 2016 +0300 +++ b/hotspot/make/test/JtregNative.gmk Mon Oct 03 21:48:21 2016 -0400 @@ -45,6 +45,7 @@ $(HOTSPOT_TOPDIR)/test/runtime/jni/8025979 \ $(HOTSPOT_TOPDIR)/test/runtime/jni/8033445 \ $(HOTSPOT_TOPDIR)/test/runtime/jni/checked \ + $(HOTSPOT_TOPDIR)/test/runtime/jni/PrivateInterfaceMethods \ $(HOTSPOT_TOPDIR)/test/runtime/jni/ToStringInInterfaceTest \ $(HOTSPOT_TOPDIR)/test/runtime/modules/getModuleJNI \ $(HOTSPOT_TOPDIR)/test/runtime/SameObject \ diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/src/share/vm/c1/c1_GraphBuilder.cpp --- a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp Wed Sep 28 18:40:50 2016 +0300 +++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp Mon Oct 03 21:48:21 2016 -0400 @@ -1929,7 +1929,7 @@ // number of implementors for decl_interface is 0 or 1. If // it's 0 then no class implements decl_interface and there's // no point in inlining. - if (!holder->is_loaded() || decl_interface->nof_implementors() != 1 || decl_interface->has_default_methods()) { + if (!holder->is_loaded() || decl_interface->nof_implementors() != 1 || decl_interface->has_nonstatic_concrete_methods()) { singleton = NULL; } } @@ -4308,7 +4308,7 @@ void GraphBuilder::profile_call(ciMethod* callee, Value recv, ciKlass* known_holder, Values* obj_args, bool inlined) { assert(known_holder == NULL || (known_holder->is_instance_klass() && (!known_holder->is_interface() || - ((ciInstanceKlass*)known_holder)->has_default_methods())), "should be default method"); + ((ciInstanceKlass*)known_holder)->has_nonstatic_concrete_methods())), "should be non-static concrete method"); if (known_holder != NULL) { if (known_holder->exact_klass() == NULL) { known_holder = compilation()->cha_exact_type(known_holder); diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/src/share/vm/ci/ciInstanceKlass.cpp --- a/hotspot/src/share/vm/ci/ciInstanceKlass.cpp Wed Sep 28 18:40:50 2016 +0300 +++ b/hotspot/src/share/vm/ci/ciInstanceKlass.cpp Mon Oct 03 21:48:21 2016 -0400 @@ -58,7 +58,7 @@ _init_state = ik->init_state(); _nonstatic_field_size = ik->nonstatic_field_size(); _has_nonstatic_fields = ik->has_nonstatic_fields(); - _has_default_methods = ik->has_default_methods(); + _has_nonstatic_concrete_methods = ik->has_nonstatic_concrete_methods(); _is_anonymous = ik->is_anonymous(); _nonstatic_fields = NULL; // initialized lazily by compute_nonstatic_fields: _has_injected_fields = -1; diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/src/share/vm/ci/ciInstanceKlass.hpp --- a/hotspot/src/share/vm/ci/ciInstanceKlass.hpp Wed Sep 28 18:40:50 2016 +0300 +++ b/hotspot/src/share/vm/ci/ciInstanceKlass.hpp Mon Oct 03 21:48:21 2016 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2016, 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 @@ -52,7 +52,7 @@ bool _has_finalizer; bool _has_subklass; bool _has_nonstatic_fields; - bool _has_default_methods; + bool _has_nonstatic_concrete_methods; bool _is_anonymous; ciFlags _flags; @@ -174,9 +174,9 @@ return 2; } } - bool has_default_methods() { + bool has_nonstatic_concrete_methods() { assert(is_loaded(), "must be loaded"); - return _has_default_methods; + return _has_nonstatic_concrete_methods; } bool is_anonymous() { diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/src/share/vm/classfile/classFileParser.cpp --- a/hotspot/src/share/vm/classfile/classFileParser.cpp Wed Sep 28 18:40:50 2016 +0300 +++ b/hotspot/src/share/vm/classfile/classFileParser.cpp Mon Oct 03 21:48:21 2016 -0400 @@ -798,11 +798,11 @@ void ClassFileParser::parse_interfaces(const ClassFileStream* const stream, const int itfs_len, ConstantPool* const cp, - bool* const has_default_methods, + bool* const has_nonstatic_concrete_methods, TRAPS) { assert(stream != NULL, "invariant"); assert(cp != NULL, "invariant"); - assert(has_default_methods != NULL, "invariant"); + assert(has_nonstatic_concrete_methods != NULL, "invariant"); if (itfs_len == 0) { _local_interfaces = Universe::the_empty_klass_array(); @@ -844,8 +844,8 @@ "Implementing class"); } - if (InstanceKlass::cast(interf())->has_default_methods()) { - *has_default_methods = true; + if (InstanceKlass::cast(interf())->has_nonstatic_concrete_methods()) { + *has_nonstatic_concrete_methods = true; } _local_interfaces->at_put(index, interf()); } @@ -2830,12 +2830,12 @@ bool is_interface, AccessFlags* promoted_flags, bool* has_final_method, - bool* declares_default_methods, + bool* declares_nonstatic_concrete_methods, TRAPS) { assert(cfs != NULL, "invariant"); assert(promoted_flags != NULL, "invariant"); assert(has_final_method != NULL, "invariant"); - assert(declares_default_methods != NULL, "invariant"); + assert(declares_nonstatic_concrete_methods != NULL, "invariant"); assert(NULL == _methods, "invariant"); @@ -2860,11 +2860,11 @@ if (method->is_final()) { *has_final_method = true; } - // declares_default_methods: declares concrete instance methods, any access flags + // declares_nonstatic_concrete_methods: declares concrete instance methods, any access flags // used for interface initialization, and default method inheritance analysis - if (is_interface && !(*declares_default_methods) + if (is_interface && !(*declares_nonstatic_concrete_methods) && !method->is_abstract() && !method->is_static()) { - *declares_default_methods = true; + *declares_nonstatic_concrete_methods = true; } _methods->at_put(index, method); } @@ -5250,8 +5250,8 @@ ik->set_minor_version(_minor_version); ik->set_major_version(_major_version); - ik->set_has_default_methods(_has_default_methods); - ik->set_declares_default_methods(_declares_default_methods); + ik->set_has_nonstatic_concrete_methods(_has_nonstatic_concrete_methods); + ik->set_declares_nonstatic_concrete_methods(_declares_nonstatic_concrete_methods); if (_host_klass != NULL) { assert (ik->is_anonymous(), "should be the same"); @@ -5311,12 +5311,9 @@ // check if this class overrides any final method check_final_method_override(ik, CHECK); - // check that if this class is an interface then it doesn't have static methods - if (ik->is_interface()) { - /* An interface in a JAVA 8 classfile can be static */ - if (_major_version < JAVA_8_VERSION) { - check_illegal_static_method(ik, CHECK); - } + // reject static interface methods prior to Java 8 + if (ik->is_interface() && _major_version < JAVA_8_VERSION) { + check_illegal_static_method(ik, CHECK); } // Obtain this_klass' module entry @@ -5336,9 +5333,9 @@ assert(_all_mirandas != NULL, "invariant"); - // Generate any default methods - default methods are interface methods - // that have a default implementation. This is new with Lambda project. - if (_has_default_methods ) { + // Generate any default methods - default methods are public interface methods + // that have a default implementation. This is new with Java 8. + if (_has_nonstatic_concrete_methods) { DefaultMethods::generate_default_methods(ik, _all_mirandas, CHECK); @@ -5523,8 +5520,8 @@ _java_fields_count(0), _need_verify(false), _relax_verify(false), - _has_default_methods(false), - _declares_default_methods(false), + _has_nonstatic_concrete_methods(false), + _declares_nonstatic_concrete_methods(false), _has_final_method(false), _has_finalizer(false), _has_empty_finalizer(false), @@ -5798,7 +5795,7 @@ parse_interfaces(stream, _itfs_len, cp, - &_has_default_methods, + &_has_nonstatic_concrete_methods, CHECK); assert(_local_interfaces != NULL, "invariant"); @@ -5821,7 +5818,7 @@ _access_flags.is_interface(), &promoted_flags, &_has_final_method, - &_declares_default_methods, + &_declares_nonstatic_concrete_methods, CHECK); assert(_methods != NULL, "invariant"); @@ -5829,8 +5826,8 @@ // promote flags from parse_methods() to the klass' flags _access_flags.add_promoted_flags(promoted_flags.as_int()); - if (_declares_default_methods) { - _has_default_methods = true; + if (_declares_nonstatic_concrete_methods) { + _has_nonstatic_concrete_methods = true; } // Additional attributes/annotations @@ -5879,8 +5876,8 @@ } if (_super_klass != NULL) { - if (_super_klass->has_default_methods()) { - _has_default_methods = true; + if (_super_klass->has_nonstatic_concrete_methods()) { + _has_nonstatic_concrete_methods = true; } if (_super_klass->is_interface()) { diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/src/share/vm/classfile/classFileParser.hpp --- a/hotspot/src/share/vm/classfile/classFileParser.hpp Wed Sep 28 18:40:50 2016 +0300 +++ b/hotspot/src/share/vm/classfile/classFileParser.hpp Mon Oct 03 21:48:21 2016 -0400 @@ -139,8 +139,8 @@ bool _need_verify; bool _relax_verify; - bool _has_default_methods; - bool _declares_default_methods; + bool _has_nonstatic_concrete_methods; + bool _declares_nonstatic_concrete_methods; bool _has_final_method; // precomputed flags @@ -186,7 +186,7 @@ void parse_interfaces(const ClassFileStream* const stream, const int itfs_len, ConstantPool* const cp, - bool* has_default_methods, + bool* has_nonstatic_concrete_methods, TRAPS); const InstanceKlass* parse_super_class(ConstantPool* const cp, @@ -224,7 +224,7 @@ bool is_interface, AccessFlags* const promoted_flags, bool* const has_final_method, - bool* const declares_default_methods, + bool* const declares_nonstatic_concrete_methods, TRAPS); const u2* parse_exception_table(const ClassFileStream* const stream, diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/src/share/vm/classfile/defaultMethods.cpp --- a/hotspot/src/share/vm/classfile/defaultMethods.cpp Wed Sep 28 18:40:50 2016 +0300 +++ b/hotspot/src/share/vm/classfile/defaultMethods.cpp Mon Oct 03 21:48:21 2016 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, 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 @@ -914,7 +914,7 @@ BytecodeBuffer buffer; if (log_is_enabled(Debug, defaultmethods)) { - ResourceMark rm; + ResourceMark rm(THREAD); outputStream* logstream = Log(defaultmethods)::debug_stream(); logstream->print("for slot: "); slot->print_on(logstream); @@ -929,6 +929,7 @@ if (method->has_target()) { Method* selected = method->get_selected_target(); if (selected->method_holder()->is_interface()) { + assert(!selected->is_private(), "pushing private interface method as default"); defaults.push(selected); } } else if (method->throws_exception()) { diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/src/share/vm/interpreter/linkResolver.cpp --- a/hotspot/src/share/vm/interpreter/linkResolver.cpp Wed Sep 28 18:40:50 2016 +0300 +++ b/hotspot/src/share/vm/interpreter/linkResolver.cpp Mon Oct 03 21:48:21 2016 -0400 @@ -858,8 +858,10 @@ } if (log_develop_is_enabled(Trace, itables)) { - trace_method_resolution("invokeinterface resolved method: caller-class", - link_info.current_klass(), resolved_klass, + char buf[200]; + jio_snprintf(buf, sizeof(buf), "%s resolved interface method: caller-class:", + Bytecodes::name(code)); + trace_method_resolution(buf, link_info.current_klass(), resolved_klass, resolved_method, true); } @@ -1424,7 +1426,7 @@ } if (log_develop_is_enabled(Trace, itables)) { - trace_method_resolution("invokeinterface selected method: receiver-class", + trace_method_resolution("invokeinterface selected method: receiver-class:", recv_klass, resolved_klass, sel_method, true); } // setup result diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/src/share/vm/oops/instanceKlass.cpp --- a/hotspot/src/share/vm/oops/instanceKlass.cpp Wed Sep 28 18:40:50 2016 +0300 +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Mon Oct 03 21:48:21 2016 -0400 @@ -674,20 +674,20 @@ // Eagerly initialize superinterfaces that declare default methods (concrete instance: any access) void InstanceKlass::initialize_super_interfaces(instanceKlassHandle this_k, TRAPS) { - assert (this_k->has_default_methods(), "caller should have checked this"); + assert (this_k->has_nonstatic_concrete_methods(), "caller should have checked this"); for (int i = 0; i < this_k->local_interfaces()->length(); ++i) { Klass* iface = this_k->local_interfaces()->at(i); InstanceKlass* ik = InstanceKlass::cast(iface); // Initialization is depth first search ie. we start with top of the inheritance tree - // has_default_methods drives searching superinterfaces since it - // means has_default_methods in its superinterface hierarchy - if (ik->has_default_methods()) { + // has_nonstatic_concrete_methods drives searching superinterfaces since it + // means has_nonstatic_concrete_methods in its superinterface hierarchy + if (ik->has_nonstatic_concrete_methods()) { ik->initialize_super_interfaces(ik, CHECK); } // Only initialize() interfaces that "declare" concrete methods. - if (ik->should_be_initialized() && ik->declares_default_methods()) { + if (ik->should_be_initialized() && ik->declares_nonstatic_concrete_methods()) { ik->initialize(CHECK); } } @@ -761,11 +761,11 @@ if (super_klass != NULL && super_klass->should_be_initialized()) { super_klass->initialize(THREAD); } - // If C implements any interfaces that declares a non-abstract, non-static method, + // If C implements any interface that declares a non-static, concrete method, // the initialization of C triggers initialization of its super interfaces. - // Only need to recurse if has_default_methods which includes declaring and - // inheriting default methods - if (!HAS_PENDING_EXCEPTION && this_k->has_default_methods()) { + // Only need to recurse if has_nonstatic_concrete_methods which includes declaring and + // having a superinterface that declares, non-static, concrete methods + if (!HAS_PENDING_EXCEPTION && this_k->has_nonstatic_concrete_methods()) { this_k->initialize_super_interfaces(this_k, THREAD); } diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/src/share/vm/oops/instanceKlass.hpp --- a/hotspot/src/share/vm/oops/instanceKlass.hpp Wed Sep 28 18:40:50 2016 +0300 +++ b/hotspot/src/share/vm/oops/instanceKlass.hpp Mon Oct 03 21:48:21 2016 -0400 @@ -207,18 +207,18 @@ // Start after _misc_kind field. enum { - _misc_rewritten = 1 << 2, // methods rewritten. - _misc_has_nonstatic_fields = 1 << 3, // for sizing with UseCompressedOops - _misc_should_verify_class = 1 << 4, // allow caching of preverification - _misc_is_anonymous = 1 << 5, // has embedded _host_klass field - _misc_is_contended = 1 << 6, // marked with contended annotation - _misc_has_default_methods = 1 << 7, // class/superclass/implemented interfaces has default methods - _misc_declares_default_methods = 1 << 8, // directly declares default methods (any access) - _misc_has_been_redefined = 1 << 9, // class has been redefined - _misc_is_scratch_class = 1 << 10, // class is the redefined scratch class - _misc_is_shared_boot_class = 1 << 11, // defining class loader is boot class loader - _misc_is_shared_platform_class = 1 << 12, // defining class loader is platform class loader - _misc_is_shared_app_class = 1 << 13 // defining class loader is app class loader + _misc_rewritten = 1 << 2, // methods rewritten. + _misc_has_nonstatic_fields = 1 << 3, // for sizing with UseCompressedOops + _misc_should_verify_class = 1 << 4, // allow caching of preverification + _misc_is_anonymous = 1 << 5, // has embedded _host_klass field + _misc_is_contended = 1 << 6, // marked with contended annotation + _misc_has_nonstatic_concrete_methods = 1 << 7, // class/superclass/implemented interfaces has non-static, concrete methods + _misc_declares_nonstatic_concrete_methods = 1 << 8, // directly declares non-static, concrete methods + _misc_has_been_redefined = 1 << 9, // class has been redefined + _misc_is_scratch_class = 1 << 10, // class is the redefined scratch class + _misc_is_shared_boot_class = 1 << 11, // defining class loader is boot class loader + _misc_is_shared_platform_class = 1 << 12, // defining class loader is platform class loader + _misc_is_shared_app_class = 1 << 13 // defining class loader is app class loader }; u2 loader_type_bits() { return _misc_is_shared_boot_class|_misc_is_shared_platform_class|_misc_is_shared_app_class; @@ -814,25 +814,25 @@ #endif // INCLUDE_JVMTI - bool has_default_methods() const { - return (_misc_flags & _misc_has_default_methods) != 0; + bool has_nonstatic_concrete_methods() const { + return (_misc_flags & _misc_has_nonstatic_concrete_methods) != 0; } - void set_has_default_methods(bool b) { + void set_has_nonstatic_concrete_methods(bool b) { if (b) { - _misc_flags |= _misc_has_default_methods; + _misc_flags |= _misc_has_nonstatic_concrete_methods; } else { - _misc_flags &= ~_misc_has_default_methods; + _misc_flags &= ~_misc_has_nonstatic_concrete_methods; } } - bool declares_default_methods() const { - return (_misc_flags & _misc_declares_default_methods) != 0; + bool declares_nonstatic_concrete_methods() const { + return (_misc_flags & _misc_declares_nonstatic_concrete_methods) != 0; } - void set_declares_default_methods(bool b) { + void set_declares_nonstatic_concrete_methods(bool b) { if (b) { - _misc_flags |= _misc_declares_default_methods; + _misc_flags |= _misc_declares_nonstatic_concrete_methods; } else { - _misc_flags &= ~_misc_declares_default_methods; + _misc_flags &= ~_misc_declares_nonstatic_concrete_methods; } } diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/src/share/vm/oops/klassVtable.cpp --- a/hotspot/src/share/vm/oops/klassVtable.cpp Wed Sep 28 18:40:50 2016 +0300 +++ b/hotspot/src/share/vm/oops/klassVtable.cpp Mon Oct 03 21:48:21 2016 -0400 @@ -226,7 +226,7 @@ HandleMark hm(THREAD); assert(default_methods->at(i)->is_method(), "must be a Method*"); methodHandle mh(THREAD, default_methods->at(i)); - + assert(!mh->is_private(), "private interface method in the default method list"); bool needs_new_entry = update_inherited_vtable(ik(), mh, super_vtable_len, i, checkconstraints, CHECK); // needs new entry @@ -362,14 +362,16 @@ Array* def_vtable_indices = NULL; bool is_default = false; - // default methods are concrete methods in superinterfaces which are added to the vtable - // with their real method_holder + + // default methods are non-private concrete methods in superinterfaces which are added + // to the vtable with their real method_holder. // Since vtable and itable indices share the same storage, don't touch - // the default method's real vtable/itable index + // the default method's real vtable/itable index. // default_vtable_indices stores the vtable value relative to this inheritor if (default_index >= 0 ) { is_default = true; def_vtable_indices = klass->default_vtable_indices(); + assert(!target_method()->is_private(), "private interface method flagged as default"); assert(def_vtable_indices != NULL, "def vtable alloc?"); assert(default_index <= def_vtable_indices->length(), "def vtable len?"); } else { @@ -395,12 +397,15 @@ // This method will either be assigned its own itable index later, // or be assigned an inherited vtable index in the loop below. // default methods inherited by classes store their vtable indices - // in the inheritor's default_vtable_indices + // in the inheritor's default_vtable_indices. // default methods inherited by interfaces may already have a - // valid itable index, if so, don't change it - // overpass methods in an interface will be assigned an itable index later - // by an inheriting class - if (!is_default || !target_method()->has_itable_index()) { + // valid itable index, if so, don't change it. + // Overpass methods in an interface will be assigned an itable index later + // by an inheriting class. + // Private interface methods have no itable index and are always invoked nonvirtually, + // so they retain their nonvirtual_vtable_index value, and therefore can_be_statically_bound() + // will return true. + if ((!is_default || !target_method()->has_itable_index()) && !target_method()->is_private()) { target_method()->set_vtable_index(Method::pending_itable_index); } } @@ -597,7 +602,9 @@ // abstract method entries using default inheritance rules if (target_method()->method_holder() != NULL && target_method()->method_holder()->is_interface() && - !target_method()->is_abstract() ) { + !target_method()->is_abstract()) { + assert(target_method()->is_default_method() || target_method()->is_private(), + "unexpected interface method type"); return false; } @@ -606,10 +613,8 @@ return true; } - // private methods in classes always have a new entry in the vtable - // specification interpretation since classic has - // private methods not overriding - // JDK8 adds private methods in interfaces which require invokespecial + // private methods in classes always have a new entry in the vtable. + // Specification interpretation since classic has private methods not overriding. if (target_method()->is_private()) { return true; } @@ -1088,6 +1093,7 @@ inline bool interface_method_needs_itable_index(Method* m) { if (m->is_static()) return false; // e.g., Stream.empty if (m->is_initializer()) return false; // or + if (m->is_private()) return false; // requires invokeSpecial // If an interface redeclares a method from java.lang.Object, // it should already have a vtable index, don't touch it. // e.g., CharSequence.toString (from initialize_vtable) diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/src/share/vm/oops/method.cpp --- a/hotspot/src/share/vm/oops/method.cpp Wed Sep 28 18:40:50 2016 +0300 +++ b/hotspot/src/share/vm/oops/method.cpp Mon Oct 03 21:48:21 2016 -0400 @@ -277,7 +277,8 @@ } address Method::bcp_from(int bci) const { - assert((is_native() && bci == 0) || (!is_native() && 0 <= bci && bci < code_size()), "illegal bci: %d", bci); + assert((is_native() && bci == 0) || (!is_native() && 0 <= bci && bci < code_size()), + "illegal bci: %d for %s method", bci, is_native() ? "native" : "non-native"); address bcp = code_base() + bci; assert(is_native() && bcp == code_base() || contains(bcp), "bcp doesn't belong to this method"); return bcp; @@ -558,7 +559,7 @@ bool Method::is_final_method(AccessFlags class_access_flags) const { // or "does_not_require_vtable_entry" // default method or overpass can occur, is not final (reuses vtable entry) - // private methods get vtable entries for backward class compatibility. + // private methods in classes get vtable entries for backward class compatibility. if (is_overpass() || is_default_method()) return false; return is_final() || class_access_flags.is_final(); } @@ -570,7 +571,7 @@ bool Method::is_default_method() const { if (method_holder() != NULL && method_holder()->is_interface() && - !is_abstract()) { + !is_abstract() && !is_private()) { return true; } else { return false; @@ -583,7 +584,9 @@ ResourceMark rm; bool is_nonv = (vtable_index() == nonvirtual_vtable_index); if (class_access_flags.is_interface()) { - assert(is_nonv == is_static(), "is_nonv=%s", name_and_sig_as_C_string()); + assert(is_nonv == is_static() || is_nonv == is_private(), + "nonvirtual unexpected for non-static, non-private: %s", + name_and_sig_as_C_string()); } #endif assert(valid_vtable_index() || valid_itable_index(), "method must be linked before we ask this question"); diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/src/share/vm/oops/method.hpp --- a/hotspot/src/share/vm/oops/method.hpp Wed Sep 28 18:40:50 2016 +0300 +++ b/hotspot/src/share/vm/oops/method.hpp Mon Oct 03 21:48:21 2016 -0400 @@ -584,6 +584,7 @@ // checks method and its method holder bool is_final_method() const; bool is_final_method(AccessFlags class_access_flags) const; + // interface method declared with 'default' - excludes private interface methods bool is_default_method() const; // true if method needs no dynamic dispatch (final and/or no vtable entry) diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/src/share/vm/prims/jni.cpp --- a/hotspot/src/share/vm/prims/jni.cpp Wed Sep 28 18:40:50 2016 +0300 +++ b/hotspot/src/share/vm/prims/jni.cpp Mon Oct 03 21:48:21 2016 -0400 @@ -1173,7 +1173,7 @@ args->set_java_argument_object(&java_args); // handle arguments - assert(!method->is_static(), "method should not be static"); + assert(!method->is_static(), "method %s should not be static", method->name_and_sig_as_C_string()); args->push_receiver(h_recv); // Push jobject handle // Fill out JavaCallArguments object diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/test/runtime/RedefineTests/RedefineInterfaceMethods.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/RedefineTests/RedefineInterfaceMethods.java Mon Oct 03 21:48:21 2016 -0400 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2016, 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 8081800 + * @summary Redefine private and default interface methods + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @modules java.compiler + * java.instrument + * jdk.jartool/sun.tools.jar + * @run main RedefineClassHelper + * @run main/othervm -javaagent:redefineagent.jar -Xlog:redefine+class*=trace RedefineInterfaceMethods + */ +public class RedefineInterfaceMethods { + + static final int RET = -2; + + static interface B { + int ORIGINAL_RETURN = 1; + int NEW_RETURN = 2; + private int privateMethod() { + return ORIGINAL_RETURN; + } + public default int defaultMethod() { + return privateMethod(); + } + } + + public static String redefinedPrivateMethod = + "interface RedefineInterfaceMethods$B {" + + " int ORIGINAL_RETURN = 1;" + + " int NEW_RETURN = 2;" + + " private int privateMethod() {" + + " return NEW_RETURN;" + + " }" + + " public default int defaultMethod() {" + + " return privateMethod();" + + " }" + + "}"; + + public static String redefinedDefaultMethod = + "interface RedefineInterfaceMethods$B {" + + " int ORIGINAL_RETURN = 1;" + + " int NEW_RETURN = 2;" + + " private int privateMethod() {" + + " return ORIGINAL_RETURN;" + + " }" + + " public default int defaultMethod() {" + + " return RedefineInterfaceMethods.RET;" + + " }" + + "}"; + + static class Impl implements B { + } + + + public static void main(String[] args) throws Exception { + + Impl impl = new Impl(); + + int res = impl.defaultMethod(); + if (res != B.ORIGINAL_RETURN) + throw new Error("defaultMethod returned " + res + + " expected " + B.ORIGINAL_RETURN); + + RedefineClassHelper.redefineClass(B.class, redefinedPrivateMethod); + + res = impl.defaultMethod(); + if (res != B.NEW_RETURN) + throw new Error("defaultMethod returned " + res + + " expected " + B.NEW_RETURN); + + System.gc(); + + RedefineClassHelper.redefineClass(B.class, redefinedDefaultMethod); + + res = impl.defaultMethod(); + if (res != RET) + throw new Error("defaultMethod returned " + res + + " expected " + RET); + } +} diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/test/runtime/jni/PrivateInterfaceMethods/PrivateInterfaceMethods.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/jni/PrivateInterfaceMethods/PrivateInterfaceMethods.java Mon Oct 03 21:48:21 2016 -0400 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2016, 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 8081800 + * @summary Add JNI invocation tests for private interface methods + * @run main/native PrivateInterfaceMethods + */ + +public class PrivateInterfaceMethods { + + static { + System.loadLibrary("PrivateInterfaceMethods"); + } + + static native int callIntVoid(Object target, String definingClassName, String methodName, boolean virtual); + + static interface A { + static final int AmResult = 1; + private int m() { return AmResult; } + } + + static interface B extends A { + // No m() here + } + + static interface C extends B { + static final int CmResult = 2; + private int m() { return CmResult; } // unrelated to A.m + } + + public static class Impl implements C { + static final int ImplmResult = 3; + private int m() { return ImplmResult; } // unrelated to A.m or C.m + } + + // We found that itable/vtable construction was affected by whether or not the + // implementation class declared a method with the same signature as the + // private interface method, so we test both variants. + + public static class Impl2 implements C { + } + + public static void main(String[] args) { + Impl impl = new Impl(); + + // Note: JNI doesn't enforce access control so we can make + // private calls not possible in Java code. + // Also it doesn't check that the receiver is a type that + // defines the method! + + // test: ((A)impl).m() - should succeed + test(impl, A.class.getName(), "m", A.AmResult, true, null); + test(impl, A.class.getName(), "m", A.AmResult, false, null); + + // test: ((B)impl).m() - should fail: NoSuchMethodError + test(impl, B.class.getName(), "m", -1, true, NoSuchMethodError.class); + test(impl, B.class.getName(), "m", -1, false, NoSuchMethodError.class); + + // test: ((C)impl).m() - should succeed + test(impl, C.class.getName(), "m", C.CmResult, true, null); + test(impl, C.class.getName(), "m", C.CmResult, false, null); + + // test: impl.m() - should succeed + test(impl, Impl.class.getName(), "m", Impl.ImplmResult, true, null); + test(impl, Impl.class.getName(), "m", Impl.ImplmResult, false, null); + + // --- + + Impl2 impl2 = new Impl2(); + + // test: ((A)impl2).m() - should succeed + test(impl2, A.class.getName(), "m", A.AmResult, true, null); + test(impl2, A.class.getName(), "m", A.AmResult, false, null); + + // test: ((B)impl2).m() - should fail: NoSuchMethodError + test(impl2, B.class.getName(), "m", -1, true, NoSuchMethodError.class); + test(impl2, B.class.getName(), "m", -1, false, NoSuchMethodError.class); + + // test: ((C)impl2).m() - should succeed + test(impl2, C.class.getName(), "m", C.CmResult, true, null); + test(impl2, C.class.getName(), "m", C.CmResult, false, null); + + // test: impl2.m() - should fail: NoSuchMethodError + test(impl2, Impl2.class.getName(), "m", -1, true, NoSuchMethodError.class); + test(impl2, Impl2.class.getName(), "m", -1, false, NoSuchMethodError.class); + } + + static void test(Object target, String definingClass, String method, + int expected, boolean virtual, Class expectedException) { + + String desc = (virtual ? "Virtual" : "Nonvirtual") + " Invocation of " + + definingClass + "." + method + " on instance of class " + + target.getClass().getName(); + try { + int res = callIntVoid(target, definingClass, method, virtual); + if (expectedException != null) + throw new Error(desc + " succeeded - but expected exception " + expectedException.getSimpleName()); + if (res != expected) + throw new Error(desc + " got wrong result: " + res + " instead of " + expected); + System.out.println(desc + " - passed"); + } + catch (Throwable t) { + if (t.getClass() != expectedException) + throw new Error(desc + " failed", t); + else + System.out.println(desc + " threw " + expectedException.getSimpleName() + " as expected"); + } + } +} diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/test/runtime/jni/PrivateInterfaceMethods/libPrivateInterfaceMethods.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/jni/PrivateInterfaceMethods/libPrivateInterfaceMethods.c Mon Oct 03 21:48:21 2016 -0400 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016, 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. + */ + +#include + +// Private interface methods call test +JNIEXPORT jint JNICALL +Java_PrivateInterfaceMethods_callIntVoid(JNIEnv *env, jclass unused, jobject impl, jstring defining_class_name, + jstring method_name, jboolean virtual) { + + // Lookup int method_name() in defining_class_name, and if it exists call impl.method_name() + // using a virtual or non-virtual invocation as indicated + + jmethodID m_id = NULL; + jclass clazz = NULL; + const char* name = NULL; + + name = (*env)->GetStringUTFChars(env, defining_class_name, NULL); + if (name == NULL) return -1; + clazz = (*env)->FindClass(env, name); + (*env)->ReleaseStringUTFChars(env, defining_class_name, name); + if ((*env)->ExceptionCheck(env)) return -1; + + name = (*env)->GetStringUTFChars(env, method_name, NULL); + if (name == NULL) return -1; + m_id = (*env)->GetMethodID(env, clazz, name, "()I"); + (*env)->ReleaseStringUTFChars(env, method_name, name); + if ((*env)->ExceptionCheck(env)) return -1; + + if (!virtual) + return (*env)->CallNonvirtualIntMethod(env, impl, clazz, m_id); + else + return (*env)->CallIntMethod(env, impl, m_id); +} diff -r e9c6bbf513e5 -r 2091069b6851 hotspot/test/runtime/logging/ItablesTest.java --- a/hotspot/test/runtime/logging/ItablesTest.java Wed Sep 28 18:40:50 2016 +0300 +++ b/hotspot/test/runtime/logging/ItablesTest.java Mon Oct 03 21:48:21 2016 -0400 @@ -47,7 +47,7 @@ output.shouldContain(": Initializing itable indices for interface "); output.shouldContain("itable index "); output.shouldContain("target: ClassB.Method1()V, method_holder: ClassB target_method flags: public"); - output.shouldContain("invokeinterface resolved method: caller-class"); + output.shouldContain("invokeinterface resolved interface method: caller-class"); output.shouldContain("invokespecial resolved method: caller-class:ClassB"); output.shouldContain("invokespecial selected method: resolved-class:ClassB"); output.shouldContain("invokeinterface selected method: receiver-class");