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/8025979 \
|
||||||
$(HOTSPOT_TOPDIR)/test/runtime/jni/8033445 \
|
$(HOTSPOT_TOPDIR)/test/runtime/jni/8033445 \
|
||||||
$(HOTSPOT_TOPDIR)/test/runtime/jni/checked \
|
$(HOTSPOT_TOPDIR)/test/runtime/jni/checked \
|
||||||
|
$(HOTSPOT_TOPDIR)/test/runtime/jni/PrivateInterfaceMethods \
|
||||||
$(HOTSPOT_TOPDIR)/test/runtime/jni/ToStringInInterfaceTest \
|
$(HOTSPOT_TOPDIR)/test/runtime/jni/ToStringInInterfaceTest \
|
||||||
$(HOTSPOT_TOPDIR)/test/runtime/modules/getModuleJNI \
|
$(HOTSPOT_TOPDIR)/test/runtime/modules/getModuleJNI \
|
||||||
$(HOTSPOT_TOPDIR)/test/runtime/SameObject \
|
$(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
|
// number of implementors for decl_interface is 0 or 1. If
|
||||||
// it's 0 then no class implements decl_interface and there's
|
// it's 0 then no class implements decl_interface and there's
|
||||||
// no point in inlining.
|
// 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;
|
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) {
|
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() &&
|
assert(known_holder == NULL || (known_holder->is_instance_klass() &&
|
||||||
(!known_holder->is_interface() ||
|
(!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 != NULL) {
|
||||||
if (known_holder->exact_klass() == NULL) {
|
if (known_holder->exact_klass() == NULL) {
|
||||||
known_holder = compilation()->cha_exact_type(known_holder);
|
known_holder = compilation()->cha_exact_type(known_holder);
|
||||||
|
@ -58,7 +58,7 @@ ciInstanceKlass::ciInstanceKlass(KlassHandle h_k) :
|
|||||||
_init_state = ik->init_state();
|
_init_state = ik->init_state();
|
||||||
_nonstatic_field_size = ik->nonstatic_field_size();
|
_nonstatic_field_size = ik->nonstatic_field_size();
|
||||||
_has_nonstatic_fields = ik->has_nonstatic_fields();
|
_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();
|
_is_anonymous = ik->is_anonymous();
|
||||||
_nonstatic_fields = NULL; // initialized lazily by compute_nonstatic_fields:
|
_nonstatic_fields = NULL; // initialized lazily by compute_nonstatic_fields:
|
||||||
_has_injected_fields = -1;
|
_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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -52,7 +52,7 @@ private:
|
|||||||
bool _has_finalizer;
|
bool _has_finalizer;
|
||||||
bool _has_subklass;
|
bool _has_subklass;
|
||||||
bool _has_nonstatic_fields;
|
bool _has_nonstatic_fields;
|
||||||
bool _has_default_methods;
|
bool _has_nonstatic_concrete_methods;
|
||||||
bool _is_anonymous;
|
bool _is_anonymous;
|
||||||
|
|
||||||
ciFlags _flags;
|
ciFlags _flags;
|
||||||
@ -174,9 +174,9 @@ public:
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool has_default_methods() {
|
bool has_nonstatic_concrete_methods() {
|
||||||
assert(is_loaded(), "must be loaded");
|
assert(is_loaded(), "must be loaded");
|
||||||
return _has_default_methods;
|
return _has_nonstatic_concrete_methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_anonymous() {
|
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,
|
void ClassFileParser::parse_interfaces(const ClassFileStream* const stream,
|
||||||
const int itfs_len,
|
const int itfs_len,
|
||||||
ConstantPool* const cp,
|
ConstantPool* const cp,
|
||||||
bool* const has_default_methods,
|
bool* const has_nonstatic_concrete_methods,
|
||||||
TRAPS) {
|
TRAPS) {
|
||||||
assert(stream != NULL, "invariant");
|
assert(stream != NULL, "invariant");
|
||||||
assert(cp != NULL, "invariant");
|
assert(cp != NULL, "invariant");
|
||||||
assert(has_default_methods != NULL, "invariant");
|
assert(has_nonstatic_concrete_methods != NULL, "invariant");
|
||||||
|
|
||||||
if (itfs_len == 0) {
|
if (itfs_len == 0) {
|
||||||
_local_interfaces = Universe::the_empty_klass_array();
|
_local_interfaces = Universe::the_empty_klass_array();
|
||||||
@ -844,8 +844,8 @@ void ClassFileParser::parse_interfaces(const ClassFileStream* const stream,
|
|||||||
"Implementing class");
|
"Implementing class");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (InstanceKlass::cast(interf())->has_default_methods()) {
|
if (InstanceKlass::cast(interf())->has_nonstatic_concrete_methods()) {
|
||||||
*has_default_methods = true;
|
*has_nonstatic_concrete_methods = true;
|
||||||
}
|
}
|
||||||
_local_interfaces->at_put(index, interf());
|
_local_interfaces->at_put(index, interf());
|
||||||
}
|
}
|
||||||
@ -2830,12 +2830,12 @@ void ClassFileParser::parse_methods(const ClassFileStream* const cfs,
|
|||||||
bool is_interface,
|
bool is_interface,
|
||||||
AccessFlags* promoted_flags,
|
AccessFlags* promoted_flags,
|
||||||
bool* has_final_method,
|
bool* has_final_method,
|
||||||
bool* declares_default_methods,
|
bool* declares_nonstatic_concrete_methods,
|
||||||
TRAPS) {
|
TRAPS) {
|
||||||
assert(cfs != NULL, "invariant");
|
assert(cfs != NULL, "invariant");
|
||||||
assert(promoted_flags != NULL, "invariant");
|
assert(promoted_flags != NULL, "invariant");
|
||||||
assert(has_final_method != 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");
|
assert(NULL == _methods, "invariant");
|
||||||
|
|
||||||
@ -2860,11 +2860,11 @@ void ClassFileParser::parse_methods(const ClassFileStream* const cfs,
|
|||||||
if (method->is_final()) {
|
if (method->is_final()) {
|
||||||
*has_final_method = true;
|
*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
|
// 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()) {
|
&& !method->is_abstract() && !method->is_static()) {
|
||||||
*declares_default_methods = true;
|
*declares_nonstatic_concrete_methods = true;
|
||||||
}
|
}
|
||||||
_methods->at_put(index, method);
|
_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_minor_version(_minor_version);
|
||||||
ik->set_major_version(_major_version);
|
ik->set_major_version(_major_version);
|
||||||
ik->set_has_default_methods(_has_default_methods);
|
ik->set_has_nonstatic_concrete_methods(_has_nonstatic_concrete_methods);
|
||||||
ik->set_declares_default_methods(_declares_default_methods);
|
ik->set_declares_nonstatic_concrete_methods(_declares_nonstatic_concrete_methods);
|
||||||
|
|
||||||
if (_host_klass != NULL) {
|
if (_host_klass != NULL) {
|
||||||
assert (ik->is_anonymous(), "should be the same");
|
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 if this class overrides any final method
|
||||||
check_final_method_override(ik, CHECK);
|
check_final_method_override(ik, CHECK);
|
||||||
|
|
||||||
// check that if this class is an interface then it doesn't have static methods
|
// reject static interface methods prior to Java 8
|
||||||
if (ik->is_interface()) {
|
if (ik->is_interface() && _major_version < JAVA_8_VERSION) {
|
||||||
/* An interface in a JAVA 8 classfile can be static */
|
check_illegal_static_method(ik, CHECK);
|
||||||
if (_major_version < JAVA_8_VERSION) {
|
|
||||||
check_illegal_static_method(ik, CHECK);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain this_klass' module entry
|
// 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");
|
assert(_all_mirandas != NULL, "invariant");
|
||||||
|
|
||||||
// Generate any default methods - default methods are interface methods
|
// Generate any default methods - default methods are public interface methods
|
||||||
// that have a default implementation. This is new with Lambda project.
|
// that have a default implementation. This is new with Java 8.
|
||||||
if (_has_default_methods ) {
|
if (_has_nonstatic_concrete_methods) {
|
||||||
DefaultMethods::generate_default_methods(ik,
|
DefaultMethods::generate_default_methods(ik,
|
||||||
_all_mirandas,
|
_all_mirandas,
|
||||||
CHECK);
|
CHECK);
|
||||||
@ -5523,8 +5520,8 @@ ClassFileParser::ClassFileParser(ClassFileStream* stream,
|
|||||||
_java_fields_count(0),
|
_java_fields_count(0),
|
||||||
_need_verify(false),
|
_need_verify(false),
|
||||||
_relax_verify(false),
|
_relax_verify(false),
|
||||||
_has_default_methods(false),
|
_has_nonstatic_concrete_methods(false),
|
||||||
_declares_default_methods(false),
|
_declares_nonstatic_concrete_methods(false),
|
||||||
_has_final_method(false),
|
_has_final_method(false),
|
||||||
_has_finalizer(false),
|
_has_finalizer(false),
|
||||||
_has_empty_finalizer(false),
|
_has_empty_finalizer(false),
|
||||||
@ -5798,7 +5795,7 @@ void ClassFileParser::parse_stream(const ClassFileStream* const stream,
|
|||||||
parse_interfaces(stream,
|
parse_interfaces(stream,
|
||||||
_itfs_len,
|
_itfs_len,
|
||||||
cp,
|
cp,
|
||||||
&_has_default_methods,
|
&_has_nonstatic_concrete_methods,
|
||||||
CHECK);
|
CHECK);
|
||||||
|
|
||||||
assert(_local_interfaces != NULL, "invariant");
|
assert(_local_interfaces != NULL, "invariant");
|
||||||
@ -5821,7 +5818,7 @@ void ClassFileParser::parse_stream(const ClassFileStream* const stream,
|
|||||||
_access_flags.is_interface(),
|
_access_flags.is_interface(),
|
||||||
&promoted_flags,
|
&promoted_flags,
|
||||||
&_has_final_method,
|
&_has_final_method,
|
||||||
&_declares_default_methods,
|
&_declares_nonstatic_concrete_methods,
|
||||||
CHECK);
|
CHECK);
|
||||||
|
|
||||||
assert(_methods != NULL, "invariant");
|
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
|
// promote flags from parse_methods() to the klass' flags
|
||||||
_access_flags.add_promoted_flags(promoted_flags.as_int());
|
_access_flags.add_promoted_flags(promoted_flags.as_int());
|
||||||
|
|
||||||
if (_declares_default_methods) {
|
if (_declares_nonstatic_concrete_methods) {
|
||||||
_has_default_methods = true;
|
_has_nonstatic_concrete_methods = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Additional attributes/annotations
|
// Additional attributes/annotations
|
||||||
@ -5879,8 +5876,8 @@ void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const st
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_super_klass != NULL) {
|
if (_super_klass != NULL) {
|
||||||
if (_super_klass->has_default_methods()) {
|
if (_super_klass->has_nonstatic_concrete_methods()) {
|
||||||
_has_default_methods = true;
|
_has_nonstatic_concrete_methods = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_super_klass->is_interface()) {
|
if (_super_klass->is_interface()) {
|
||||||
|
@ -139,8 +139,8 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
|
|||||||
bool _need_verify;
|
bool _need_verify;
|
||||||
bool _relax_verify;
|
bool _relax_verify;
|
||||||
|
|
||||||
bool _has_default_methods;
|
bool _has_nonstatic_concrete_methods;
|
||||||
bool _declares_default_methods;
|
bool _declares_nonstatic_concrete_methods;
|
||||||
bool _has_final_method;
|
bool _has_final_method;
|
||||||
|
|
||||||
// precomputed flags
|
// precomputed flags
|
||||||
@ -186,7 +186,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
|
|||||||
void parse_interfaces(const ClassFileStream* const stream,
|
void parse_interfaces(const ClassFileStream* const stream,
|
||||||
const int itfs_len,
|
const int itfs_len,
|
||||||
ConstantPool* const cp,
|
ConstantPool* const cp,
|
||||||
bool* has_default_methods,
|
bool* has_nonstatic_concrete_methods,
|
||||||
TRAPS);
|
TRAPS);
|
||||||
|
|
||||||
const InstanceKlass* parse_super_class(ConstantPool* const cp,
|
const InstanceKlass* parse_super_class(ConstantPool* const cp,
|
||||||
@ -224,7 +224,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
|
|||||||
bool is_interface,
|
bool is_interface,
|
||||||
AccessFlags* const promoted_flags,
|
AccessFlags* const promoted_flags,
|
||||||
bool* const has_final_method,
|
bool* const has_final_method,
|
||||||
bool* const declares_default_methods,
|
bool* const declares_nonstatic_concrete_methods,
|
||||||
TRAPS);
|
TRAPS);
|
||||||
|
|
||||||
const u2* parse_exception_table(const ClassFileStream* const stream,
|
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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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;
|
BytecodeBuffer buffer;
|
||||||
|
|
||||||
if (log_is_enabled(Debug, defaultmethods)) {
|
if (log_is_enabled(Debug, defaultmethods)) {
|
||||||
ResourceMark rm;
|
ResourceMark rm(THREAD);
|
||||||
outputStream* logstream = Log(defaultmethods)::debug_stream();
|
outputStream* logstream = Log(defaultmethods)::debug_stream();
|
||||||
logstream->print("for slot: ");
|
logstream->print("for slot: ");
|
||||||
slot->print_on(logstream);
|
slot->print_on(logstream);
|
||||||
@ -929,6 +929,7 @@ static void create_defaults_and_exceptions(
|
|||||||
if (method->has_target()) {
|
if (method->has_target()) {
|
||||||
Method* selected = method->get_selected_target();
|
Method* selected = method->get_selected_target();
|
||||||
if (selected->method_holder()->is_interface()) {
|
if (selected->method_holder()->is_interface()) {
|
||||||
|
assert(!selected->is_private(), "pushing private interface method as default");
|
||||||
defaults.push(selected);
|
defaults.push(selected);
|
||||||
}
|
}
|
||||||
} else if (method->throws_exception()) {
|
} 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)) {
|
if (log_develop_is_enabled(Trace, itables)) {
|
||||||
trace_method_resolution("invokeinterface resolved method: caller-class",
|
char buf[200];
|
||||||
link_info.current_klass(), resolved_klass,
|
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);
|
resolved_method, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1424,7 +1426,7 @@ void LinkResolver::runtime_resolve_interface_method(CallInfo& result,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (log_develop_is_enabled(Trace, itables)) {
|
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);
|
recv_klass, resolved_klass, sel_method, true);
|
||||||
}
|
}
|
||||||
// setup result
|
// setup result
|
||||||
|
@ -674,20 +674,20 @@ void InstanceKlass::link_methods(TRAPS) {
|
|||||||
|
|
||||||
// Eagerly initialize superinterfaces that declare default methods (concrete instance: any access)
|
// Eagerly initialize superinterfaces that declare default methods (concrete instance: any access)
|
||||||
void InstanceKlass::initialize_super_interfaces(instanceKlassHandle this_k, TRAPS) {
|
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) {
|
for (int i = 0; i < this_k->local_interfaces()->length(); ++i) {
|
||||||
Klass* iface = this_k->local_interfaces()->at(i);
|
Klass* iface = this_k->local_interfaces()->at(i);
|
||||||
InstanceKlass* ik = InstanceKlass::cast(iface);
|
InstanceKlass* ik = InstanceKlass::cast(iface);
|
||||||
|
|
||||||
// Initialization is depth first search ie. we start with top of the inheritance tree
|
// Initialization is depth first search ie. we start with top of the inheritance tree
|
||||||
// has_default_methods drives searching superinterfaces since it
|
// has_nonstatic_concrete_methods drives searching superinterfaces since it
|
||||||
// means has_default_methods in its superinterface hierarchy
|
// means has_nonstatic_concrete_methods in its superinterface hierarchy
|
||||||
if (ik->has_default_methods()) {
|
if (ik->has_nonstatic_concrete_methods()) {
|
||||||
ik->initialize_super_interfaces(ik, CHECK);
|
ik->initialize_super_interfaces(ik, CHECK);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only initialize() interfaces that "declare" concrete methods.
|
// 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);
|
ik->initialize(CHECK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -761,11 +761,11 @@ void InstanceKlass::initialize_impl(instanceKlassHandle this_k, TRAPS) {
|
|||||||
if (super_klass != NULL && super_klass->should_be_initialized()) {
|
if (super_klass != NULL && super_klass->should_be_initialized()) {
|
||||||
super_klass->initialize(THREAD);
|
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.
|
// the initialization of C triggers initialization of its super interfaces.
|
||||||
// Only need to recurse if has_default_methods which includes declaring and
|
// Only need to recurse if has_nonstatic_concrete_methods which includes declaring and
|
||||||
// inheriting default methods
|
// having a superinterface that declares, non-static, concrete methods
|
||||||
if (!HAS_PENDING_EXCEPTION && this_k->has_default_methods()) {
|
if (!HAS_PENDING_EXCEPTION && this_k->has_nonstatic_concrete_methods()) {
|
||||||
this_k->initialize_super_interfaces(this_k, THREAD);
|
this_k->initialize_super_interfaces(this_k, THREAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,18 +207,18 @@ class InstanceKlass: public Klass {
|
|||||||
|
|
||||||
// Start after _misc_kind field.
|
// Start after _misc_kind field.
|
||||||
enum {
|
enum {
|
||||||
_misc_rewritten = 1 << 2, // methods rewritten.
|
_misc_rewritten = 1 << 2, // methods rewritten.
|
||||||
_misc_has_nonstatic_fields = 1 << 3, // for sizing with UseCompressedOops
|
_misc_has_nonstatic_fields = 1 << 3, // for sizing with UseCompressedOops
|
||||||
_misc_should_verify_class = 1 << 4, // allow caching of preverification
|
_misc_should_verify_class = 1 << 4, // allow caching of preverification
|
||||||
_misc_is_anonymous = 1 << 5, // has embedded _host_klass field
|
_misc_is_anonymous = 1 << 5, // has embedded _host_klass field
|
||||||
_misc_is_contended = 1 << 6, // marked with contended annotation
|
_misc_is_contended = 1 << 6, // marked with contended annotation
|
||||||
_misc_has_default_methods = 1 << 7, // class/superclass/implemented interfaces has default methods
|
_misc_has_nonstatic_concrete_methods = 1 << 7, // class/superclass/implemented interfaces has non-static, concrete methods
|
||||||
_misc_declares_default_methods = 1 << 8, // directly declares default methods (any access)
|
_misc_declares_nonstatic_concrete_methods = 1 << 8, // directly declares non-static, concrete methods
|
||||||
_misc_has_been_redefined = 1 << 9, // class has been redefined
|
_misc_has_been_redefined = 1 << 9, // class has been redefined
|
||||||
_misc_is_scratch_class = 1 << 10, // class is the redefined scratch class
|
_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_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_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_is_shared_app_class = 1 << 13 // defining class loader is app class loader
|
||||||
};
|
};
|
||||||
u2 loader_type_bits() {
|
u2 loader_type_bits() {
|
||||||
return _misc_is_shared_boot_class|_misc_is_shared_platform_class|_misc_is_shared_app_class;
|
return _misc_is_shared_boot_class|_misc_is_shared_platform_class|_misc_is_shared_app_class;
|
||||||
@ -814,25 +814,25 @@ public:
|
|||||||
|
|
||||||
#endif // INCLUDE_JVMTI
|
#endif // INCLUDE_JVMTI
|
||||||
|
|
||||||
bool has_default_methods() const {
|
bool has_nonstatic_concrete_methods() const {
|
||||||
return (_misc_flags & _misc_has_default_methods) != 0;
|
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) {
|
if (b) {
|
||||||
_misc_flags |= _misc_has_default_methods;
|
_misc_flags |= _misc_has_nonstatic_concrete_methods;
|
||||||
} else {
|
} else {
|
||||||
_misc_flags &= ~_misc_has_default_methods;
|
_misc_flags &= ~_misc_has_nonstatic_concrete_methods;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool declares_default_methods() const {
|
bool declares_nonstatic_concrete_methods() const {
|
||||||
return (_misc_flags & _misc_declares_default_methods) != 0;
|
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) {
|
if (b) {
|
||||||
_misc_flags |= _misc_declares_default_methods;
|
_misc_flags |= _misc_declares_nonstatic_concrete_methods;
|
||||||
} else {
|
} 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);
|
HandleMark hm(THREAD);
|
||||||
assert(default_methods->at(i)->is_method(), "must be a Method*");
|
assert(default_methods->at(i)->is_method(), "must be a Method*");
|
||||||
methodHandle mh(THREAD, default_methods->at(i));
|
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);
|
bool needs_new_entry = update_inherited_vtable(ik(), mh, super_vtable_len, i, checkconstraints, CHECK);
|
||||||
|
|
||||||
// needs new entry
|
// needs new entry
|
||||||
@ -362,14 +362,16 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
|
|||||||
|
|
||||||
Array<int>* def_vtable_indices = NULL;
|
Array<int>* def_vtable_indices = NULL;
|
||||||
bool is_default = false;
|
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
|
// 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
|
// default_vtable_indices stores the vtable value relative to this inheritor
|
||||||
if (default_index >= 0 ) {
|
if (default_index >= 0 ) {
|
||||||
is_default = true;
|
is_default = true;
|
||||||
def_vtable_indices = klass->default_vtable_indices();
|
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(def_vtable_indices != NULL, "def vtable alloc?");
|
||||||
assert(default_index <= def_vtable_indices->length(), "def vtable len?");
|
assert(default_index <= def_vtable_indices->length(), "def vtable len?");
|
||||||
} else {
|
} 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,
|
// This method will either be assigned its own itable index later,
|
||||||
// or be assigned an inherited vtable index in the loop below.
|
// or be assigned an inherited vtable index in the loop below.
|
||||||
// default methods inherited by classes store their vtable indices
|
// 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
|
// default methods inherited by interfaces may already have a
|
||||||
// valid itable index, if so, don't change it
|
// valid itable index, if so, don't change it.
|
||||||
// overpass methods in an interface will be assigned an itable index later
|
// Overpass methods in an interface will be assigned an itable index later
|
||||||
// by an inheriting class
|
// by an inheriting class.
|
||||||
if (!is_default || !target_method()->has_itable_index()) {
|
// 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);
|
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
|
// abstract method entries using default inheritance rules
|
||||||
if (target_method()->method_holder() != NULL &&
|
if (target_method()->method_holder() != NULL &&
|
||||||
target_method()->method_holder()->is_interface() &&
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -606,10 +613,8 @@ bool klassVtable::needs_new_vtable_entry(methodHandle target_method,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private methods in classes always have a new entry in the vtable
|
// private methods in classes always have a new entry in the vtable.
|
||||||
// specification interpretation since classic has
|
// Specification interpretation since classic has private methods not overriding.
|
||||||
// private methods not overriding
|
|
||||||
// JDK8 adds private methods in interfaces which require invokespecial
|
|
||||||
if (target_method()->is_private()) {
|
if (target_method()->is_private()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1088,6 +1093,7 @@ void klassItable::initialize_itable(bool checkconstraints, TRAPS) {
|
|||||||
inline bool interface_method_needs_itable_index(Method* m) {
|
inline bool interface_method_needs_itable_index(Method* m) {
|
||||||
if (m->is_static()) return false; // e.g., Stream.empty
|
if (m->is_static()) return false; // e.g., Stream.empty
|
||||||
if (m->is_initializer()) return false; // <init> or <clinit>
|
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,
|
// If an interface redeclares a method from java.lang.Object,
|
||||||
// it should already have a vtable index, don't touch it.
|
// it should already have a vtable index, don't touch it.
|
||||||
// e.g., CharSequence.toString (from initialize_vtable)
|
// 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 {
|
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;
|
address bcp = code_base() + bci;
|
||||||
assert(is_native() && bcp == code_base() || contains(bcp), "bcp doesn't belong to this method");
|
assert(is_native() && bcp == code_base() || contains(bcp), "bcp doesn't belong to this method");
|
||||||
return bcp;
|
return bcp;
|
||||||
@ -558,7 +559,7 @@ bool Method::compute_has_loops_flag() {
|
|||||||
bool Method::is_final_method(AccessFlags class_access_flags) const {
|
bool Method::is_final_method(AccessFlags class_access_flags) const {
|
||||||
// or "does_not_require_vtable_entry"
|
// or "does_not_require_vtable_entry"
|
||||||
// default method or overpass can occur, is not final (reuses 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;
|
if (is_overpass() || is_default_method()) return false;
|
||||||
return is_final() || class_access_flags.is_final();
|
return is_final() || class_access_flags.is_final();
|
||||||
}
|
}
|
||||||
@ -570,7 +571,7 @@ bool Method::is_final_method() const {
|
|||||||
bool Method::is_default_method() const {
|
bool Method::is_default_method() const {
|
||||||
if (method_holder() != NULL &&
|
if (method_holder() != NULL &&
|
||||||
method_holder()->is_interface() &&
|
method_holder()->is_interface() &&
|
||||||
!is_abstract()) {
|
!is_abstract() && !is_private()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -583,7 +584,9 @@ bool Method::can_be_statically_bound(AccessFlags class_access_flags) const {
|
|||||||
ResourceMark rm;
|
ResourceMark rm;
|
||||||
bool is_nonv = (vtable_index() == nonvirtual_vtable_index);
|
bool is_nonv = (vtable_index() == nonvirtual_vtable_index);
|
||||||
if (class_access_flags.is_interface()) {
|
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
|
#endif
|
||||||
assert(valid_vtable_index() || valid_itable_index(), "method must be linked before we ask this question");
|
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
|
// checks method and its method holder
|
||||||
bool is_final_method() const;
|
bool is_final_method() const;
|
||||||
bool is_final_method(AccessFlags class_access_flags) const;
|
bool is_final_method(AccessFlags class_access_flags) const;
|
||||||
|
// interface method declared with 'default' - excludes private interface methods
|
||||||
bool is_default_method() const;
|
bool is_default_method() const;
|
||||||
|
|
||||||
// true if method needs no dynamic dispatch (final and/or no vtable entry)
|
// 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);
|
args->set_java_argument_object(&java_args);
|
||||||
|
|
||||||
// handle arguments
|
// 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
|
args->push_receiver(h_recv); // Push jobject handle
|
||||||
|
|
||||||
// Fill out JavaCallArguments object
|
// 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(": Initializing itable indices for interface ");
|
||||||
output.shouldContain("itable index ");
|
output.shouldContain("itable index ");
|
||||||
output.shouldContain("target: ClassB.Method1()V, method_holder: ClassB target_method flags: public");
|
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 resolved method: caller-class:ClassB");
|
||||||
output.shouldContain("invokespecial selected method: resolved-class:ClassB");
|
output.shouldContain("invokespecial selected method: resolved-class:ClassB");
|
||||||
output.shouldContain("invokeinterface selected method: receiver-class");
|
output.shouldContain("invokeinterface selected method: receiver-class");
|
||||||
|
Loading…
Reference in New Issue
Block a user