8081800: AbstractMethodError when evaluating a private method in an interface via debugger
Reviewed-by: acorn, dcubed, coleenp
--- 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 \
--- 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);
--- 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;
--- 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() {
--- 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()) {
--- 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,
--- 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()) {
--- 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
--- 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);
}
--- 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;
}
}
--- 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<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 @@
// 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; // <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)
--- 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");
--- 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)
--- 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
--- /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);
+ }
+}
--- /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");
+ }
+ }
+}
--- /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 <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);
+}
--- 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");