8081800: AbstractMethodError when evaluating a private method in an interface via debugger

Reviewed-by: acorn, dcubed, coleenp
This commit is contained in:
David Holmes 2016-10-03 21:48:21 -04:00
parent e67d5a890c
commit d1856645bc
18 changed files with 394 additions and 95 deletions

View File

@ -45,6 +45,7 @@ BUILD_HOTSPOT_JTREG_NATIVE_SRC := \
$(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 \

View File

@ -1929,7 +1929,7 @@ void GraphBuilder::invoke(Bytecodes::Code code) {
// 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::print_stats() {
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);

View File

@ -58,7 +58,7 @@ ciInstanceKlass::ciInstanceKlass(KlassHandle h_k) :
_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;

View File

@ -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 @@ private:
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 @@ public:
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() {

View File

@ -798,11 +798,11 @@ static bool put_after_lookup(const Symbol* name, const Symbol* sig, NameSigHash*
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 @@ void ClassFileParser::parse_interfaces(const ClassFileStream* const stream,
"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 @@ void ClassFileParser::parse_methods(const ClassFileStream* const cfs,
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 @@ void ClassFileParser::parse_methods(const ClassFileStream* const cfs,
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 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loa
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 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loa
// 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 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loa
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 @@ ClassFileParser::ClassFileParser(ClassFileStream* stream,
_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 @@ void ClassFileParser::parse_stream(const ClassFileStream* const stream,
parse_interfaces(stream,
_itfs_len,
cp,
&_has_default_methods,
&_has_nonstatic_concrete_methods,
CHECK);
assert(_local_interfaces != NULL, "invariant");
@ -5821,7 +5818,7 @@ void ClassFileParser::parse_stream(const ClassFileStream* const stream,
_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 @@ void ClassFileParser::parse_stream(const ClassFileStream* const stream,
// 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 @@ void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const st
}
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()) {

View File

@ -139,8 +139,8 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
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 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
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 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
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,

View File

@ -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 @@ static void create_defaults_and_exceptions(
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 @@ static void create_defaults_and_exceptions(
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()) {

View File

@ -858,8 +858,10 @@ methodHandle LinkResolver::resolve_interface_method(const LinkInfo& link_info, B
}
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 @@ void LinkResolver::runtime_resolve_interface_method(CallInfo& result,
}
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

View File

@ -674,20 +674,20 @@ void InstanceKlass::link_methods(TRAPS) {
// 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 @@ void InstanceKlass::initialize_impl(instanceKlassHandle this_k, TRAPS) {
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);
}

View File

@ -207,18 +207,18 @@ class InstanceKlass: public Klass {
// 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 @@ public:
#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;
}
}

View File

@ -226,7 +226,7 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) {
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 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
Array<int>* 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 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
// 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 @@ bool klassVtable::needs_new_vtable_entry(methodHandle target_method,
// 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 @@ bool klassVtable::needs_new_vtable_entry(methodHandle target_method,
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 @@ void klassItable::initialize_itable(bool checkconstraints, TRAPS) {
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; // <init> or <clinit>
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)

View File

@ -277,7 +277,8 @@ int Method::validate_bci_from_bcp(address bcp) const {
}
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::compute_has_loops_flag() {
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_final_method() const {
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 @@ bool Method::can_be_statically_bound(AccessFlags class_access_flags) const {
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");

View File

@ -584,6 +584,7 @@ class Method : public Metadata {
// 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)

View File

@ -1173,7 +1173,7 @@ static void jni_invoke_nonstatic(JNIEnv *env, JavaValue* result, jobject receive
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

View File

@ -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);
}
}

View File

@ -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");
}
}
}

View File

@ -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 <jni.h>
// 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);
}

View File

@ -47,7 +47,7 @@ public class ItablesTest {
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");