8081800: AbstractMethodError when evaluating a private method in an interface via debugger
Reviewed-by: acorn, dcubed, coleenp
This commit is contained in:
parent
e67d5a890c
commit
d1856645bc
@ -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 \
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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()) {
|
||||
|
@ -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,
|
||||
|
@ -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()) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
104
hotspot/test/runtime/RedefineTests/RedefineInterfaceMethods.java
Normal file
104
hotspot/test/runtime/RedefineTests/RedefineInterfaceMethods.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user