mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-11 05:59:52 +00:00
8033150: invokestatic: IncompatibleClassChangeError trying to invoke static method from a parent in presence of conflicting defaults
A static method should be preferred during method resolution over an overpass, search the current class as well as its superclasses. Reviewed-by: acorn, coleenp, kamg
This commit is contained in:
parent
5ca274e0ce
commit
8c36d0cd2b
@ -390,20 +390,6 @@ class MethodFamily : public ResourceObj {
|
||||
Symbol* get_exception_message() { return _exception_message; }
|
||||
Symbol* get_exception_name() { return _exception_name; }
|
||||
|
||||
// Return true if the specified klass has a static method that matches
|
||||
// the name and signature of the target method.
|
||||
bool has_matching_static(InstanceKlass* root) {
|
||||
if (_members.length() > 0) {
|
||||
Pair<Method*,QualifiedState> entry = _members.at(0);
|
||||
Method* impl = root->find_method(entry.first->name(),
|
||||
entry.first->signature());
|
||||
if ((impl != NULL) && impl->is_static()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Either sets the target or the exception error message
|
||||
void determine_target(InstanceKlass* root, TRAPS) {
|
||||
if (has_target() || throws_exception()) {
|
||||
@ -433,21 +419,19 @@ class MethodFamily : public ResourceObj {
|
||||
// If the root klass has a static method with matching name and signature
|
||||
// then do not generate an overpass method because it will hide the
|
||||
// static method during resolution.
|
||||
if (!has_matching_static(root)) {
|
||||
if (qualified_methods.length() == 0) {
|
||||
_exception_message = generate_no_defaults_message(CHECK);
|
||||
} else {
|
||||
assert(root != NULL, "Null root class");
|
||||
_exception_message = generate_method_message(root->name(), qualified_methods.at(0), CHECK);
|
||||
}
|
||||
_exception_name = vmSymbols::java_lang_AbstractMethodError();
|
||||
if (qualified_methods.length() == 0) {
|
||||
_exception_message = generate_no_defaults_message(CHECK);
|
||||
} else {
|
||||
assert(root != NULL, "Null root class");
|
||||
_exception_message = generate_method_message(root->name(), qualified_methods.at(0), CHECK);
|
||||
}
|
||||
_exception_name = vmSymbols::java_lang_AbstractMethodError();
|
||||
|
||||
// If only one qualified method is default, select that
|
||||
} else if (num_defaults == 1) {
|
||||
_selected_target = qualified_methods.at(default_index);
|
||||
|
||||
} else if (num_defaults > 1 && !has_matching_static(root)) {
|
||||
} else if (num_defaults > 1) {
|
||||
_exception_message = generate_conflicts_message(&qualified_methods,CHECK);
|
||||
_exception_name = vmSymbols::java_lang_IncompatibleClassChangeError();
|
||||
if (TraceDefaultMethods) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2014, 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
|
||||
@ -1943,7 +1943,7 @@ bool ClassVerifier::is_protected_access(instanceKlassHandle this_class,
|
||||
InstanceKlass* target_instance = InstanceKlass::cast(target_class);
|
||||
fieldDescriptor fd;
|
||||
if (is_method) {
|
||||
Method* m = target_instance->uncached_lookup_method(field_name, field_sig);
|
||||
Method* m = target_instance->uncached_lookup_method(field_name, field_sig, Klass::normal);
|
||||
if (m != NULL && m->is_protected()) {
|
||||
if (!this_class->is_same_class_package(m->method_holder())) {
|
||||
return true;
|
||||
@ -2280,7 +2280,8 @@ void ClassVerifier::verify_invoke_init(
|
||||
ref_class_type.name(), CHECK_VERIFY(this));
|
||||
Method* m = InstanceKlass::cast(ref_klass)->uncached_lookup_method(
|
||||
vmSymbols::object_initializer_name(),
|
||||
cp->signature_ref_at(bcs->get_index_u2()));
|
||||
cp->signature_ref_at(bcs->get_index_u2()),
|
||||
Klass::normal);
|
||||
instanceKlassHandle mh(THREAD, m->method_holder());
|
||||
if (m->is_protected() && !mh->is_same_class_package(_klass())) {
|
||||
bool assignable = current_type().is_assignable_from(
|
||||
|
||||
@ -243,7 +243,8 @@ void LinkResolver::resolve_klass(KlassHandle& result, constantPoolHandle pool, i
|
||||
// Look up method in klasses, including static methods
|
||||
// Then look up local default methods
|
||||
void LinkResolver::lookup_method_in_klasses(methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, bool checkpolymorphism, bool in_imethod_resolve, TRAPS) {
|
||||
Method* result_oop = klass->uncached_lookup_method(name, signature);
|
||||
// Ignore overpasses so statics can be found during resolution
|
||||
Method* result_oop = klass->uncached_lookup_method(name, signature, Klass::skip_overpass);
|
||||
|
||||
// JDK 8, JVMS 5.4.3.4: Interface method resolution should
|
||||
// ignore static and non-public methods of java.lang.Object,
|
||||
@ -256,6 +257,12 @@ void LinkResolver::lookup_method_in_klasses(methodHandle& result, KlassHandle kl
|
||||
result_oop = NULL;
|
||||
}
|
||||
|
||||
// Before considering default methods, check for an overpass in the
|
||||
// current class if a method has not been found.
|
||||
if (result_oop == NULL) {
|
||||
result_oop = InstanceKlass::cast(klass())->find_method(name, signature);
|
||||
}
|
||||
|
||||
if (result_oop == NULL) {
|
||||
Array<Method*>* default_methods = InstanceKlass::cast(klass())->default_methods();
|
||||
if (default_methods != NULL) {
|
||||
@ -276,11 +283,11 @@ void LinkResolver::lookup_method_in_klasses(methodHandle& result, KlassHandle kl
|
||||
// returns first instance method
|
||||
// Looks up method in classes, then looks up local default methods
|
||||
void LinkResolver::lookup_instance_method_in_klasses(methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS) {
|
||||
Method* result_oop = klass->uncached_lookup_method(name, signature);
|
||||
Method* result_oop = klass->uncached_lookup_method(name, signature, Klass::normal);
|
||||
result = methodHandle(THREAD, result_oop);
|
||||
while (!result.is_null() && result->is_static() && result->method_holder()->super() != NULL) {
|
||||
KlassHandle super_klass = KlassHandle(THREAD, result->method_holder()->super());
|
||||
result = methodHandle(THREAD, super_klass->uncached_lookup_method(name, signature));
|
||||
result = methodHandle(THREAD, super_klass->uncached_lookup_method(name, signature, Klass::normal));
|
||||
}
|
||||
|
||||
if (result.is_null()) {
|
||||
@ -302,7 +309,7 @@ int LinkResolver::vtable_index_of_interface_method(KlassHandle klass,
|
||||
// First check in default method array
|
||||
if (!resolved_method->is_abstract() &&
|
||||
(InstanceKlass::cast(klass())->default_methods() != NULL)) {
|
||||
int index = InstanceKlass::find_method_index(InstanceKlass::cast(klass())->default_methods(), name, signature);
|
||||
int index = InstanceKlass::find_method_index(InstanceKlass::cast(klass())->default_methods(), name, signature, false);
|
||||
if (index >= 0 ) {
|
||||
vtable_index = InstanceKlass::cast(klass())->default_vtable_indices()->at(index);
|
||||
}
|
||||
@ -322,7 +329,7 @@ void LinkResolver::lookup_method_in_interfaces(methodHandle& result, KlassHandle
|
||||
// Specify 'true' in order to skip default methods when searching the
|
||||
// interfaces. Function lookup_method_in_klasses() already looked for
|
||||
// the method in the default methods table.
|
||||
result = methodHandle(THREAD, ik->lookup_method_in_all_interfaces(name, signature, true));
|
||||
result = methodHandle(THREAD, ik->lookup_method_in_all_interfaces(name, signature, Klass::skip_defaults));
|
||||
}
|
||||
|
||||
void LinkResolver::lookup_polymorphic_method(methodHandle& result,
|
||||
|
||||
@ -64,10 +64,10 @@ oop ArrayKlass::multi_allocate(int rank, jint* sizes, TRAPS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Method* ArrayKlass::uncached_lookup_method(Symbol* name, Symbol* signature) const {
|
||||
Method* ArrayKlass::uncached_lookup_method(Symbol* name, Symbol* signature, MethodLookupMode mode) const {
|
||||
// There are no methods in an array klass but the super class (Object) has some
|
||||
assert(super(), "super klass must be present");
|
||||
return super()->uncached_lookup_method(name, signature);
|
||||
return super()->uncached_lookup_method(name, signature, mode);
|
||||
}
|
||||
|
||||
ArrayKlass::ArrayKlass(Symbol* name) {
|
||||
|
||||
@ -86,7 +86,7 @@ class ArrayKlass: public Klass {
|
||||
objArrayOop allocate_arrayArray(int n, int length, TRAPS);
|
||||
|
||||
// Lookup operations
|
||||
Method* uncached_lookup_method(Symbol* name, Symbol* signature) const;
|
||||
Method* uncached_lookup_method(Symbol* name, Symbol* signature, MethodLookupMode mode) const;
|
||||
|
||||
// Casting from Klass*
|
||||
static ArrayKlass* cast(Klass* k) {
|
||||
|
||||
@ -1389,7 +1389,11 @@ static int binary_search(Array<Method*>* methods, Symbol* name) {
|
||||
|
||||
// find_method looks up the name/signature in the local methods array
|
||||
Method* InstanceKlass::find_method(Symbol* name, Symbol* signature) const {
|
||||
return InstanceKlass::find_method(methods(), name, signature);
|
||||
return find_method_impl(name, signature, false);
|
||||
}
|
||||
|
||||
Method* InstanceKlass::find_method_impl(Symbol* name, Symbol* signature, bool skipping_overpass) const {
|
||||
return InstanceKlass::find_method_impl(methods(), name, signature, skipping_overpass);
|
||||
}
|
||||
|
||||
// find_instance_method looks up the name/signature in the local methods array
|
||||
@ -1406,40 +1410,49 @@ Method* InstanceKlass::find_instance_method(
|
||||
// find_method looks up the name/signature in the local methods array
|
||||
Method* InstanceKlass::find_method(
|
||||
Array<Method*>* methods, Symbol* name, Symbol* signature) {
|
||||
int hit = find_method_index(methods, name, signature);
|
||||
return InstanceKlass::find_method_impl(methods, name, signature, false);
|
||||
}
|
||||
|
||||
Method* InstanceKlass::find_method_impl(
|
||||
Array<Method*>* methods, Symbol* name, Symbol* signature, bool skipping_overpass) {
|
||||
int hit = find_method_index(methods, name, signature, skipping_overpass);
|
||||
return hit >= 0 ? methods->at(hit): NULL;
|
||||
}
|
||||
|
||||
// Used directly for default_methods to find the index into the
|
||||
// default_vtable_indices, and indirectly by find_method
|
||||
// find_method_index looks in the local methods array to return the index
|
||||
// of the matching name/signature
|
||||
// of the matching name/signature. If, overpass methods are being ignored,
|
||||
// the search continues to find a potential non-overpass match. This capability
|
||||
// is important during method resolution to prefer a static method, for example,
|
||||
// over an overpass method.
|
||||
int InstanceKlass::find_method_index(
|
||||
Array<Method*>* methods, Symbol* name, Symbol* signature) {
|
||||
Array<Method*>* methods, Symbol* name, Symbol* signature, bool skipping_overpass) {
|
||||
int hit = binary_search(methods, name);
|
||||
if (hit != -1) {
|
||||
Method* m = methods->at(hit);
|
||||
// Do linear search to find matching signature. First, quick check
|
||||
// for common case
|
||||
if (m->signature() == signature) return hit;
|
||||
// for common case, ignoring overpasses if requested.
|
||||
if ((m->signature() == signature) && (!skipping_overpass || !m->is_overpass())) return hit;
|
||||
|
||||
// search downwards through overloaded methods
|
||||
int i;
|
||||
for (i = hit - 1; i >= 0; --i) {
|
||||
Method* m = methods->at(i);
|
||||
assert(m->is_method(), "must be method");
|
||||
if (m->name() != name) break;
|
||||
if (m->signature() == signature) return i;
|
||||
if ((m->signature() == signature) && (!skipping_overpass || !m->is_overpass())) return i;
|
||||
}
|
||||
// search upwards
|
||||
for (i = hit + 1; i < methods->length(); ++i) {
|
||||
Method* m = methods->at(i);
|
||||
assert(m->is_method(), "must be method");
|
||||
if (m->name() != name) break;
|
||||
if (m->signature() == signature) return i;
|
||||
if ((m->signature() == signature) && (!skipping_overpass || !m->is_overpass())) return i;
|
||||
}
|
||||
// not found
|
||||
#ifdef ASSERT
|
||||
int index = linear_search(methods, name, signature);
|
||||
int index = skipping_overpass ? -1 : linear_search(methods, name, signature);
|
||||
assert(index == -1, err_msg("binary search should have found entry %d", index));
|
||||
#endif
|
||||
}
|
||||
@ -1465,16 +1478,16 @@ int InstanceKlass::find_method_by_name(
|
||||
|
||||
// uncached_lookup_method searches both the local class methods array and all
|
||||
// superclasses methods arrays, skipping any overpass methods in superclasses.
|
||||
Method* InstanceKlass::uncached_lookup_method(Symbol* name, Symbol* signature) const {
|
||||
Method* InstanceKlass::uncached_lookup_method(Symbol* name, Symbol* signature, MethodLookupMode mode) const {
|
||||
MethodLookupMode lookup_mode = mode;
|
||||
Klass* klass = const_cast<InstanceKlass*>(this);
|
||||
bool dont_ignore_overpasses = true; // For the class being searched, find its overpasses.
|
||||
while (klass != NULL) {
|
||||
Method* method = InstanceKlass::cast(klass)->find_method(name, signature);
|
||||
if ((method != NULL) && (dont_ignore_overpasses || !method->is_overpass())) {
|
||||
Method* method = InstanceKlass::cast(klass)->find_method_impl(name, signature, (lookup_mode == skip_overpass));
|
||||
if (method != NULL) {
|
||||
return method;
|
||||
}
|
||||
klass = InstanceKlass::cast(klass)->super();
|
||||
dont_ignore_overpasses = false; // Ignore overpass methods in all superclasses.
|
||||
lookup_mode = skip_overpass; // Always ignore overpass methods in superclasses
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -1489,7 +1502,7 @@ Method* InstanceKlass::lookup_method_in_ordered_interfaces(Symbol* name,
|
||||
}
|
||||
// Look up interfaces
|
||||
if (m == NULL) {
|
||||
m = lookup_method_in_all_interfaces(name, signature, false);
|
||||
m = lookup_method_in_all_interfaces(name, signature, normal);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
@ -1499,7 +1512,7 @@ Method* InstanceKlass::lookup_method_in_ordered_interfaces(Symbol* name,
|
||||
// They should only be found in the initial InterfaceMethodRef
|
||||
Method* InstanceKlass::lookup_method_in_all_interfaces(Symbol* name,
|
||||
Symbol* signature,
|
||||
bool skip_default_methods) const {
|
||||
MethodLookupMode mode) const {
|
||||
Array<Klass*>* all_ifs = transitive_interfaces();
|
||||
int num_ifs = all_ifs->length();
|
||||
InstanceKlass *ik = NULL;
|
||||
@ -1507,7 +1520,7 @@ Method* InstanceKlass::lookup_method_in_all_interfaces(Symbol* name,
|
||||
ik = InstanceKlass::cast(all_ifs->at(i));
|
||||
Method* m = ik->lookup_method(name, signature);
|
||||
if (m != NULL && m->is_public() && !m->is_static() &&
|
||||
(!skip_default_methods || !m->is_default_method())) {
|
||||
((mode != skip_defaults) || !m->is_default_method())) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
@ -490,14 +490,14 @@ class InstanceKlass: public Klass {
|
||||
static Method* find_instance_method(Array<Method*>* methods, Symbol* name, Symbol* signature);
|
||||
|
||||
// find a local method index in default_methods (returns -1 if not found)
|
||||
static int find_method_index(Array<Method*>* methods, Symbol* name, Symbol* signature);
|
||||
static int find_method_index(Array<Method*>* methods, Symbol* name, Symbol* signature, bool skipping_overpass);
|
||||
|
||||
// lookup operation (returns NULL if not found)
|
||||
Method* uncached_lookup_method(Symbol* name, Symbol* signature) const;
|
||||
Method* uncached_lookup_method(Symbol* name, Symbol* signature, MethodLookupMode mode) const;
|
||||
|
||||
// lookup a method in all the interfaces that this class implements
|
||||
// (returns NULL if not found)
|
||||
Method* lookup_method_in_all_interfaces(Symbol* name, Symbol* signature, bool skip_default_methods) const;
|
||||
Method* lookup_method_in_all_interfaces(Symbol* name, Symbol* signature, MethodLookupMode mode) const;
|
||||
|
||||
// lookup a method in local defaults then in all interfaces
|
||||
// (returns NULL if not found)
|
||||
@ -1020,6 +1020,10 @@ private:
|
||||
// Returns the array class with this class as element type
|
||||
Klass* array_klass_impl(bool or_null, TRAPS);
|
||||
|
||||
// find a local method (returns NULL if not found)
|
||||
Method* find_method_impl(Symbol* name, Symbol* signature, bool skipping_overpass) const;
|
||||
static Method* find_method_impl(Array<Method*>* methods, Symbol* name, Symbol* signature, bool skipping_overpass);
|
||||
|
||||
// Free CHeap allocated fields.
|
||||
void release_C_heap_structures();
|
||||
public:
|
||||
|
||||
@ -129,7 +129,7 @@ bool Klass::compute_is_subtype_of(Klass* k) {
|
||||
}
|
||||
|
||||
|
||||
Method* Klass::uncached_lookup_method(Symbol* name, Symbol* signature) const {
|
||||
Method* Klass::uncached_lookup_method(Symbol* name, Symbol* signature, MethodLookupMode mode) const {
|
||||
#ifdef ASSERT
|
||||
tty->print_cr("Error: uncached_lookup_method called on a klass oop."
|
||||
" Likely error: reflection method does not correctly"
|
||||
|
||||
@ -154,6 +154,8 @@ class Klass : public Metadata {
|
||||
void* operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw();
|
||||
|
||||
public:
|
||||
enum MethodLookupMode { normal, skip_overpass, skip_defaults };
|
||||
|
||||
bool is_klass() const volatile { return true; }
|
||||
|
||||
// super
|
||||
@ -391,10 +393,10 @@ class Klass : public Metadata {
|
||||
virtual void initialize(TRAPS);
|
||||
// lookup operation for MethodLookupCache
|
||||
friend class MethodLookupCache;
|
||||
virtual Method* uncached_lookup_method(Symbol* name, Symbol* signature) const;
|
||||
virtual Method* uncached_lookup_method(Symbol* name, Symbol* signature, MethodLookupMode mode) const;
|
||||
public:
|
||||
Method* lookup_method(Symbol* name, Symbol* signature) const {
|
||||
return uncached_lookup_method(name, signature);
|
||||
return uncached_lookup_method(name, signature, normal);
|
||||
}
|
||||
|
||||
// array class with specific rank
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2014, 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
|
||||
@ -622,7 +622,7 @@ bool klassVtable::needs_new_vtable_entry(methodHandle target_method,
|
||||
// this check for all access permissions.
|
||||
InstanceKlass *sk = InstanceKlass::cast(super);
|
||||
if (sk->has_miranda_methods()) {
|
||||
if (sk->lookup_method_in_all_interfaces(name, signature, false) != NULL) {
|
||||
if (sk->lookup_method_in_all_interfaces(name, signature, Klass::normal) != NULL) {
|
||||
return false; // found a matching miranda; we do not need a new entry
|
||||
}
|
||||
}
|
||||
@ -698,7 +698,7 @@ bool klassVtable::is_miranda(Method* m, Array<Method*>* class_methods,
|
||||
&& mo->method_holder() != NULL
|
||||
&& mo->method_holder()->super() != NULL)
|
||||
{
|
||||
mo = mo->method_holder()->super()->uncached_lookup_method(name, signature);
|
||||
mo = mo->method_holder()->super()->uncached_lookup_method(name, signature, Klass::normal);
|
||||
}
|
||||
if (mo == NULL || mo->access_flags().is_private() ) {
|
||||
// super class hierarchy does not implement it or protection is different
|
||||
@ -743,7 +743,7 @@ void klassVtable::add_new_mirandas_to_lists(
|
||||
if (is_miranda(im, class_methods, default_methods, super)) { // is it a miranda at all?
|
||||
InstanceKlass *sk = InstanceKlass::cast(super);
|
||||
// check if it is a duplicate of a super's miranda
|
||||
if (sk->lookup_method_in_all_interfaces(im->name(), im->signature(), false) == NULL) {
|
||||
if (sk->lookup_method_in_all_interfaces(im->name(), im->signature(), Klass::normal) == NULL) {
|
||||
new_mirandas->append(im);
|
||||
}
|
||||
if (all_mirandas != NULL) {
|
||||
|
||||
@ -1215,7 +1215,8 @@ JVM_ENTRY(jobject, JVM_DoPrivileged(JNIEnv *env, jclass cls, jobject action, job
|
||||
// get run() method
|
||||
Method* m_oop = object->klass()->uncached_lookup_method(
|
||||
vmSymbols::run_method_name(),
|
||||
vmSymbols::void_object_signature());
|
||||
vmSymbols::void_object_signature(),
|
||||
Klass::normal);
|
||||
methodHandle m (THREAD, m_oop);
|
||||
if (m.is_null() || !m->is_method() || !m()->is_public() || m()->is_static()) {
|
||||
THROW_MSG_0(vmSymbols::java_lang_InternalError(), "No run method");
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2014, 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
|
||||
@ -408,7 +408,7 @@ address NativeLookup::base_library_lookup(const char* class_name, const char* me
|
||||
|
||||
// Find method and invoke standard lookup
|
||||
methodHandle method (THREAD,
|
||||
klass->uncached_lookup_method(m_name, s_name));
|
||||
klass->uncached_lookup_method(m_name, s_name, Klass::normal));
|
||||
address result = lookup(method, in_base_library, CATCH);
|
||||
assert(in_base_library, "must be in basic library");
|
||||
guarantee(result != NULL, "must be non NULL");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user