mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8011311: Private interface methods. Default conflicts:ICCE. no erased_super_default
Reviewed-by: coleenp, bharadwaj, minqi
This commit is contained in:
parent
bfafab7b47
commit
71a3a55630
6 changed files with 200 additions and 268 deletions
|
@ -2545,7 +2545,9 @@ Array<Method*>* ClassFileParser::parse_methods(bool is_interface,
|
|||
if (method->is_final()) {
|
||||
*has_final_method = true;
|
||||
}
|
||||
if (is_interface && !method->is_abstract() && !method->is_static()) {
|
||||
if (is_interface && !(*has_default_methods)
|
||||
&& !method->is_abstract() && !method->is_static()
|
||||
&& !method->is_private()) {
|
||||
// default method
|
||||
*has_default_methods = true;
|
||||
}
|
||||
|
|
|
@ -325,6 +325,7 @@ class MethodFamily : public ResourceObj {
|
|||
|
||||
Method* _selected_target; // Filled in later, if a unique target exists
|
||||
Symbol* _exception_message; // If no unique target is found
|
||||
Symbol* _exception_name; // If no unique target is found
|
||||
|
||||
bool contains_method(Method* method) {
|
||||
int* lookup = _member_index.get(method);
|
||||
|
@ -350,7 +351,7 @@ class MethodFamily : public ResourceObj {
|
|||
public:
|
||||
|
||||
MethodFamily()
|
||||
: _selected_target(NULL), _exception_message(NULL) {}
|
||||
: _selected_target(NULL), _exception_message(NULL), _exception_name(NULL) {}
|
||||
|
||||
void set_target_if_empty(Method* m) {
|
||||
if (_selected_target == NULL && !m->is_overpass()) {
|
||||
|
@ -383,6 +384,7 @@ class MethodFamily : public ResourceObj {
|
|||
|
||||
Method* get_selected_target() { return _selected_target; }
|
||||
Symbol* get_exception_message() { return _exception_message; }
|
||||
Symbol* get_exception_name() { return _exception_name; }
|
||||
|
||||
// Either sets the target or the exception error message
|
||||
void determine_target(InstanceKlass* root, TRAPS) {
|
||||
|
@ -400,15 +402,18 @@ class MethodFamily : public ResourceObj {
|
|||
|
||||
if (qualified_methods.length() == 0) {
|
||||
_exception_message = generate_no_defaults_message(CHECK);
|
||||
_exception_name = vmSymbols::java_lang_AbstractMethodError();
|
||||
} else if (qualified_methods.length() == 1) {
|
||||
Method* method = qualified_methods.at(0);
|
||||
if (method->is_abstract()) {
|
||||
_exception_message = generate_abstract_method_message(method, CHECK);
|
||||
_exception_name = vmSymbols::java_lang_AbstractMethodError();
|
||||
} else {
|
||||
_selected_target = qualified_methods.at(0);
|
||||
}
|
||||
} else {
|
||||
_exception_message = generate_conflicts_message(&qualified_methods,CHECK);
|
||||
_exception_name = vmSymbols::java_lang_IncompatibleClassChangeError();
|
||||
}
|
||||
|
||||
assert((has_target() ^ throws_exception()) == 1,
|
||||
|
@ -459,8 +464,9 @@ class MethodFamily : public ResourceObj {
|
|||
|
||||
void print_exception(outputStream* str, int indent) {
|
||||
assert(throws_exception(), "Should be called otherwise");
|
||||
assert(_exception_name != NULL, "exception_name should be set");
|
||||
streamIndentor si(str, indent * 2);
|
||||
str->indent().print_cr("%s", _exception_message->as_C_string());
|
||||
str->indent().print_cr("%s: %s", _exception_name->as_C_string(), _exception_message->as_C_string());
|
||||
}
|
||||
#endif // ndef PRODUCT
|
||||
};
|
||||
|
@ -670,7 +676,10 @@ class FindMethodsByErasedSig : public HierarchyVisitor<FindMethodsByErasedSig> {
|
|||
InstanceKlass* iklass = current_class();
|
||||
|
||||
Method* m = iklass->find_method(_method_name, _method_signature);
|
||||
if (m != NULL) {
|
||||
// private interface methods are not candidates for default methods
|
||||
// invokespecial to private interface methods doesn't use default method logic
|
||||
// future: take access controls into account for superclass methods
|
||||
if (m != NULL && (!iklass->is_interface() || m->is_public())) {
|
||||
if (_family == NULL) {
|
||||
_family = new StatefulMethodFamily();
|
||||
}
|
||||
|
@ -782,200 +791,7 @@ void DefaultMethods::generate_default_methods(
|
|||
#endif // ndef PRODUCT
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface inheritance rules were used to find a unique default method
|
||||
* candidate for the resolved class. This
|
||||
* method is only viable if it would also be in the set of default method
|
||||
* candidates if we ran a full analysis on the current class.
|
||||
*
|
||||
* The only reason that the method would not be in the set of candidates for
|
||||
* the current class is if that there's another matching method
|
||||
* which is "more specific" than the found method -- i.e., one could find a
|
||||
* path in the interface hierarchy in which the matching method appears
|
||||
* before we get to '_target'.
|
||||
*
|
||||
* In order to determine this, we examine all of the implemented
|
||||
* interfaces. If we find path that leads to the '_target' interface, then
|
||||
* we examine that path to see if there are any methods that would shadow
|
||||
* the selected method along that path.
|
||||
*/
|
||||
class ShadowChecker : public HierarchyVisitor<ShadowChecker> {
|
||||
protected:
|
||||
Thread* THREAD;
|
||||
|
||||
InstanceKlass* _target;
|
||||
|
||||
Symbol* _method_name;
|
||||
InstanceKlass* _method_holder;
|
||||
bool _found_shadow;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
ShadowChecker(Thread* thread, Symbol* name, InstanceKlass* holder,
|
||||
InstanceKlass* target)
|
||||
: THREAD(thread), _method_name(name), _method_holder(holder),
|
||||
_target(target), _found_shadow(false) {}
|
||||
|
||||
void* new_node_data(InstanceKlass* cls) { return NULL; }
|
||||
void free_node_data(void* data) { return; }
|
||||
|
||||
bool visit() {
|
||||
InstanceKlass* ik = current_class();
|
||||
if (ik == _target && current_depth() == 1) {
|
||||
return false; // This was the specified super -- no need to search it
|
||||
}
|
||||
if (ik == _method_holder || ik == _target) {
|
||||
// We found a path that should be examined to see if it shadows _method
|
||||
if (path_has_shadow()) {
|
||||
_found_shadow = true;
|
||||
cancel_iteration();
|
||||
}
|
||||
return false; // no need to continue up hierarchy
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool path_has_shadow() = 0;
|
||||
bool found_shadow() { return _found_shadow; }
|
||||
};
|
||||
|
||||
// Used for Invokespecial.
|
||||
// Invokespecial is allowed to invoke a concrete interface method
|
||||
// and can be used to disambuiguate among qualified candidates,
|
||||
// which are methods in immediate superinterfaces,
|
||||
// but may not be used to invoke a candidate that would be shadowed
|
||||
// from the perspective of the caller.
|
||||
// Invokespecial is also used in the overpass generation today
|
||||
// We re-run the shadowchecker because we can't distinguish this case,
|
||||
// but it should return the same answer, since the overpass target
|
||||
// is now the invokespecial caller.
|
||||
class ErasedShadowChecker : public ShadowChecker {
|
||||
private:
|
||||
bool path_has_shadow() {
|
||||
|
||||
for (int i = current_depth() - 1; i > 0; --i) {
|
||||
InstanceKlass* ik = class_at_depth(i);
|
||||
|
||||
if (ik->is_interface()) {
|
||||
int end;
|
||||
int start = ik->find_method_by_name(_method_name, &end);
|
||||
if (start != -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public:
|
||||
|
||||
ErasedShadowChecker(Thread* thread, Symbol* name, InstanceKlass* holder,
|
||||
InstanceKlass* target)
|
||||
: ShadowChecker(thread, name, holder, target) {}
|
||||
};
|
||||
|
||||
// Find the unique qualified candidate from the perspective of the super_class
|
||||
// which is the resolved_klass, which must be an immediate superinterface
|
||||
// of klass
|
||||
Method* find_erased_super_default(InstanceKlass* current_class, InstanceKlass* super_class, Symbol* method_name, Symbol* sig, TRAPS) {
|
||||
|
||||
FindMethodsByErasedSig visitor(method_name, sig);
|
||||
visitor.run(super_class); // find candidates from resolved_klass
|
||||
|
||||
MethodFamily* family;
|
||||
visitor.get_discovered_family(&family);
|
||||
|
||||
if (family != NULL) {
|
||||
family->determine_target(current_class, CHECK_NULL); // get target from current_class
|
||||
|
||||
if (family->has_target()) {
|
||||
Method* target = family->get_selected_target();
|
||||
InstanceKlass* holder = InstanceKlass::cast(target->method_holder());
|
||||
|
||||
// Verify that the identified method is valid from the context of
|
||||
// the current class, which is the caller class for invokespecial
|
||||
// link resolution, i.e. ensure there it is not shadowed.
|
||||
// You can use invokespecial to disambiguate interface methods, but
|
||||
// you can not use it to skip over an interface method that would shadow it.
|
||||
ErasedShadowChecker checker(THREAD, target->name(), holder, super_class);
|
||||
checker.run(current_class);
|
||||
|
||||
if (checker.found_shadow()) {
|
||||
#ifndef PRODUCT
|
||||
if (TraceDefaultMethods) {
|
||||
tty->print_cr(" Only candidate found was shadowed.");
|
||||
}
|
||||
#endif // ndef PRODUCT
|
||||
THROW_MSG_(vmSymbols::java_lang_AbstractMethodError(),
|
||||
"Accessible default method not found", NULL);
|
||||
} else {
|
||||
#ifndef PRODUCT
|
||||
if (TraceDefaultMethods) {
|
||||
family->print_sig_on(tty, target->signature(), 1);
|
||||
}
|
||||
#endif // ndef PRODUCT
|
||||
return target;
|
||||
}
|
||||
} else {
|
||||
assert(family->throws_exception(), "must have target or throw");
|
||||
THROW_MSG_(vmSymbols::java_lang_AbstractMethodError(),
|
||||
family->get_exception_message()->as_C_string(), NULL);
|
||||
}
|
||||
} else {
|
||||
// no method found
|
||||
ResourceMark rm(THREAD);
|
||||
THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(),
|
||||
Method::name_and_sig_as_C_string(current_class,
|
||||
method_name, sig), NULL);
|
||||
}
|
||||
}
|
||||
// This is called during linktime when we find an invokespecial call that
|
||||
// refers to a direct superinterface. It indicates that we should find the
|
||||
// default method in the hierarchy of that superinterface, and if that method
|
||||
// would have been a candidate from the point of view of 'this' class, then we
|
||||
// return that method.
|
||||
// This logic assumes that the super is a direct superclass of the caller
|
||||
Method* DefaultMethods::find_super_default(
|
||||
Klass* cls, Klass* super, Symbol* method_name, Symbol* sig, TRAPS) {
|
||||
|
||||
ResourceMark rm(THREAD);
|
||||
|
||||
assert(cls != NULL && super != NULL, "Need real classes");
|
||||
|
||||
InstanceKlass* current_class = InstanceKlass::cast(cls);
|
||||
InstanceKlass* super_class = InstanceKlass::cast(super);
|
||||
|
||||
// Keep entire hierarchy alive for the duration of the computation
|
||||
KeepAliveRegistrar keepAlive(THREAD);
|
||||
KeepAliveVisitor loadKeepAlive(&keepAlive);
|
||||
loadKeepAlive.run(current_class); // get hierarchy from current class
|
||||
|
||||
#ifndef PRODUCT
|
||||
if (TraceDefaultMethods) {
|
||||
tty->print_cr("Finding super default method %s.%s%s from %s",
|
||||
super_class->name()->as_C_string(),
|
||||
method_name->as_C_string(), sig->as_C_string(),
|
||||
current_class->name()->as_C_string());
|
||||
}
|
||||
#endif // ndef PRODUCT
|
||||
|
||||
assert(super_class->is_interface(), "only call for default methods");
|
||||
|
||||
Method* target = NULL;
|
||||
target = find_erased_super_default(current_class, super_class,
|
||||
method_name, sig, CHECK_NULL);
|
||||
|
||||
#ifndef PRODUCT
|
||||
if (target != NULL) {
|
||||
if (TraceDefaultMethods) {
|
||||
tty->print(" Returning ");
|
||||
print_method(tty, target, true);
|
||||
tty->print_cr("");
|
||||
}
|
||||
}
|
||||
#endif // ndef PRODUCT
|
||||
return target;
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
// Return true is broad type is a covariant return of narrow type
|
||||
|
@ -1035,10 +851,9 @@ static int assemble_redirect(
|
|||
return parameter_count;
|
||||
}
|
||||
|
||||
static int assemble_abstract_method_error(
|
||||
BytecodeConstantPool* cp, BytecodeBuffer* buffer, Symbol* message, TRAPS) {
|
||||
static int assemble_method_error(
|
||||
BytecodeConstantPool* cp, BytecodeBuffer* buffer, Symbol* errorName, Symbol* message, TRAPS) {
|
||||
|
||||
Symbol* errorName = vmSymbols::java_lang_AbstractMethodError();
|
||||
Symbol* init = vmSymbols::object_initializer_name();
|
||||
Symbol* sig = vmSymbols::string_void_signature();
|
||||
|
||||
|
@ -1150,8 +965,7 @@ static void create_overpasses(
|
|||
&bpool, &buffer, slot->signature(), selected, CHECK);
|
||||
}
|
||||
} else if (method->throws_exception()) {
|
||||
max_stack = assemble_abstract_method_error(
|
||||
&bpool, &buffer, method->get_exception_message(), CHECK);
|
||||
max_stack = assemble_method_error(&bpool, &buffer, method->get_exception_name(), method->get_exception_message(), CHECK);
|
||||
}
|
||||
if (max_stack != 0) {
|
||||
AccessFlags flags = accessFlags_from(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2013, 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
|
||||
|
@ -44,15 +44,5 @@ class DefaultMethods : AllStatic {
|
|||
// the class.
|
||||
static void generate_default_methods(
|
||||
InstanceKlass* klass, GrowableArray<Method*>* mirandas, TRAPS);
|
||||
|
||||
|
||||
// Called during linking when an invokespecial to an direct interface
|
||||
// method is found. Selects and returns a method if there is a unique
|
||||
// default method in the 'super_iface' part of the hierarchy which is
|
||||
// also a candidate default for 'this_klass'. Otherwise throws an AME.
|
||||
static Method* find_super_default(
|
||||
Klass* this_klass, Klass* super_iface,
|
||||
Symbol* method_name, Symbol* method_sig, TRAPS);
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_CLASSFILE_DEFAULTMETHODS_HPP
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/*
|
||||
* Copyright (c) 1997, 2013, 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
|
||||
|
@ -573,6 +572,16 @@ void LinkResolver::resolve_interface_method(methodHandle& resolved_method,
|
|||
}
|
||||
|
||||
if (check_access) {
|
||||
// JDK8 adds non-public interface methods, and accessability check requirement
|
||||
assert(current_klass.not_null() , "current_klass should not be null");
|
||||
|
||||
// check if method can be accessed by the referring class
|
||||
check_method_accessability(current_klass,
|
||||
resolved_klass,
|
||||
KlassHandle(THREAD, resolved_method->method_holder()),
|
||||
resolved_method,
|
||||
CHECK);
|
||||
|
||||
HandleMark hm(THREAD);
|
||||
Handle loader (THREAD, InstanceKlass::cast(current_klass())->class_loader());
|
||||
Handle class_loader (THREAD, resolved_method->method_holder()->class_loader());
|
||||
|
@ -604,6 +613,20 @@ void LinkResolver::resolve_interface_method(methodHandle& resolved_method,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TraceItables && Verbose) {
|
||||
ResourceMark rm(THREAD);
|
||||
tty->print("invokeinterface resolved method: caller-class:%s, compile-time-class:%s, method:%s, method_holder:%s, access_flags: ",
|
||||
(current_klass.is_null() ? "<NULL>" : current_klass->internal_name()),
|
||||
(resolved_klass.is_null() ? "<NULL>" : resolved_klass->internal_name()),
|
||||
Method::name_and_sig_as_C_string(resolved_klass(),
|
||||
resolved_method->name(),
|
||||
resolved_method->signature()),
|
||||
resolved_method->method_holder()->internal_name()
|
||||
);
|
||||
resolved_method->access_flags().print_on(tty);
|
||||
tty->cr();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -795,26 +818,12 @@ void LinkResolver::linktime_resolve_special_method(methodHandle& resolved_method
|
|||
Symbol* method_name, Symbol* method_signature,
|
||||
KlassHandle current_klass, bool check_access, TRAPS) {
|
||||
|
||||
if (resolved_klass->is_interface() && current_klass() != NULL) {
|
||||
// If the target class is a direct interface, treat this as a "super"
|
||||
// default call.
|
||||
//
|
||||
// If the current method is an overpass that happens to call a direct
|
||||
// super-interface's method, then we'll end up rerunning the default method
|
||||
// analysis even though we don't need to, but that's ok since it will end
|
||||
// up with the same answer.
|
||||
InstanceKlass* ik = InstanceKlass::cast(current_klass());
|
||||
Array<Klass*>* interfaces = ik->local_interfaces();
|
||||
int num_interfaces = interfaces->length();
|
||||
for (int index = 0; index < num_interfaces; index++) {
|
||||
if (interfaces->at(index) == resolved_klass()) {
|
||||
Method* method = DefaultMethods::find_super_default(current_klass(),
|
||||
resolved_klass(), method_name, method_signature, CHECK);
|
||||
resolved_method = methodHandle(THREAD, method);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Invokespecial is called for multiple special reasons:
|
||||
// <init>
|
||||
// local private method invocation, for classes and interfaces
|
||||
// superclass.method, which can also resolve to a default method
|
||||
// and the selected method is recalculated relative to the direct superclass
|
||||
// superinterface.method, which explicitly does not check shadowing
|
||||
|
||||
resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, check_access, CHECK);
|
||||
|
||||
|
@ -844,6 +853,26 @@ void LinkResolver::linktime_resolve_special_method(methodHandle& resolved_method
|
|||
resolved_method->signature()));
|
||||
THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf);
|
||||
}
|
||||
if (TraceItables && Verbose) {
|
||||
ResourceMark rm(THREAD);
|
||||
tty->print("invokespecial resolved method: caller-class:%s, compile-time-class:%s, method:%s, method_holder:%s, access_flags: ",
|
||||
(current_klass.is_null() ? "<NULL>" : current_klass->internal_name()),
|
||||
(resolved_klass.is_null() ? "<NULL>" : resolved_klass->internal_name()),
|
||||
Method::name_and_sig_as_C_string(resolved_klass(),
|
||||
resolved_method->name(),
|
||||
resolved_method->signature()),
|
||||
resolved_method->method_holder()->internal_name()
|
||||
);
|
||||
resolved_method->access_flags().print_on(tty);
|
||||
if (resolved_method->method_holder()->is_interface() &&
|
||||
!resolved_method->is_abstract()) {
|
||||
tty->print("default");
|
||||
}
|
||||
if (resolved_method->is_overpass()) {
|
||||
tty->print("overpass");
|
||||
}
|
||||
tty->cr();
|
||||
}
|
||||
}
|
||||
|
||||
// throws runtime exceptions
|
||||
|
@ -851,23 +880,24 @@ void LinkResolver::runtime_resolve_special_method(CallInfo& result, methodHandle
|
|||
KlassHandle current_klass, bool check_access, TRAPS) {
|
||||
|
||||
// resolved method is selected method unless we have an old-style lookup
|
||||
// for a superclass method
|
||||
// Invokespecial for a superinterface, resolved method is selected method,
|
||||
// no checks for shadowing
|
||||
methodHandle sel_method(THREAD, resolved_method());
|
||||
|
||||
// check if this is an old-style super call and do a new lookup if so
|
||||
{ KlassHandle method_klass = KlassHandle(THREAD,
|
||||
resolved_method->method_holder());
|
||||
|
||||
const bool direct_calling_default_method =
|
||||
resolved_klass() != NULL && resolved_method() != NULL &&
|
||||
resolved_klass->is_interface() && !resolved_method->is_abstract();
|
||||
|
||||
if (!direct_calling_default_method &&
|
||||
check_access &&
|
||||
if (check_access &&
|
||||
// a) check if ACC_SUPER flag is set for the current class
|
||||
(current_klass->is_super() || !AllowNonVirtualCalls) &&
|
||||
// b) check if the method class is a superclass of the current class (superclass relation is not reflexive!)
|
||||
current_klass->is_subtype_of(method_klass()) &&
|
||||
current_klass() != method_klass() &&
|
||||
// b) check if the class of the resolved_klass is a superclass
|
||||
// (not supertype in order to exclude interface classes) of the current class.
|
||||
// This check is not performed for super.invoke for interface methods
|
||||
// in super interfaces.
|
||||
current_klass->is_subclass_of(resolved_klass()) &&
|
||||
current_klass() != resolved_klass() &&
|
||||
// c) check if the method is not <init>
|
||||
resolved_method->name() != vmSymbols::object_initializer_name()) {
|
||||
// Lookup super method
|
||||
|
@ -905,6 +935,23 @@ void LinkResolver::runtime_resolve_special_method(CallInfo& result, methodHandle
|
|||
sel_method->signature()));
|
||||
}
|
||||
|
||||
if (TraceItables && Verbose) {
|
||||
ResourceMark rm(THREAD);
|
||||
tty->print("invokespecial selected method: resolved-class:%s, method:%s, method_holder:%s, access_flags: ",
|
||||
(resolved_klass.is_null() ? "<NULL>" : resolved_klass->internal_name()),
|
||||
Method::name_and_sig_as_C_string(resolved_klass(),
|
||||
sel_method->name(),
|
||||
sel_method->signature()),
|
||||
sel_method->method_holder()->internal_name()
|
||||
);
|
||||
sel_method->access_flags().print_on(tty);
|
||||
if (sel_method->method_holder()->is_interface() &&
|
||||
!sel_method->is_abstract()) {
|
||||
tty->print("default");
|
||||
}
|
||||
tty->cr();
|
||||
}
|
||||
|
||||
// setup result
|
||||
result.set_static(resolved_klass, sel_method, CHECK);
|
||||
}
|
||||
|
@ -927,6 +974,18 @@ void LinkResolver::linktime_resolve_virtual_method(methodHandle &resolved_method
|
|||
assert(resolved_method->name() != vmSymbols::object_initializer_name(), "should have been checked in verifier");
|
||||
assert(resolved_method->name() != vmSymbols::class_initializer_name (), "should have been checked in verifier");
|
||||
|
||||
// check if private interface method
|
||||
if (resolved_klass->is_interface() && resolved_method->is_private()) {
|
||||
ResourceMark rm(THREAD);
|
||||
char buf[200];
|
||||
jio_snprintf(buf, sizeof(buf), "private interface method requires invokespecial, not invokevirtual: method %s, caller-class:%s",
|
||||
Method::name_and_sig_as_C_string(resolved_klass(),
|
||||
resolved_method->name(),
|
||||
resolved_method->signature()),
|
||||
(current_klass.is_null() ? "<NULL>" : current_klass->internal_name()));
|
||||
THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf);
|
||||
}
|
||||
|
||||
// check if not static
|
||||
if (resolved_method->is_static()) {
|
||||
ResourceMark rm(THREAD);
|
||||
|
@ -936,6 +995,27 @@ void LinkResolver::linktime_resolve_virtual_method(methodHandle &resolved_method
|
|||
resolved_method->signature()));
|
||||
THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf);
|
||||
}
|
||||
|
||||
if (PrintVtables && Verbose) {
|
||||
ResourceMark rm(THREAD);
|
||||
tty->print("invokevirtual resolved method: caller-class:%s, compile-time-class:%s, method:%s, method_holder:%s, access_flags: ",
|
||||
(current_klass.is_null() ? "<NULL>" : current_klass->internal_name()),
|
||||
(resolved_klass.is_null() ? "<NULL>" : resolved_klass->internal_name()),
|
||||
Method::name_and_sig_as_C_string(resolved_klass(),
|
||||
resolved_method->name(),
|
||||
resolved_method->signature()),
|
||||
resolved_method->method_holder()->internal_name()
|
||||
);
|
||||
resolved_method->access_flags().print_on(tty);
|
||||
if (resolved_method->method_holder()->is_interface() &&
|
||||
!resolved_method->is_abstract()) {
|
||||
tty->print("default");
|
||||
}
|
||||
if (resolved_method->is_overpass()) {
|
||||
tty->print("overpass");
|
||||
}
|
||||
tty->cr();
|
||||
}
|
||||
}
|
||||
|
||||
// throws runtime exceptions
|
||||
|
@ -1012,6 +1092,27 @@ void LinkResolver::runtime_resolve_virtual_method(CallInfo& result,
|
|||
selected_method->signature()));
|
||||
}
|
||||
|
||||
if (PrintVtables && Verbose) {
|
||||
ResourceMark rm(THREAD);
|
||||
tty->print("invokevirtual selected method: receiver-class:%s, resolved-class:%s, method:%s, method_holder:%s, vtable_index:%d, access_flags: ",
|
||||
(recv_klass.is_null() ? "<NULL>" : recv_klass->internal_name()),
|
||||
(resolved_klass.is_null() ? "<NULL>" : resolved_klass->internal_name()),
|
||||
Method::name_and_sig_as_C_string(resolved_klass(),
|
||||
resolved_method->name(),
|
||||
resolved_method->signature()),
|
||||
selected_method->method_holder()->internal_name(),
|
||||
vtable_index
|
||||
);
|
||||
selected_method->access_flags().print_on(tty);
|
||||
if (selected_method->method_holder()->is_interface() &&
|
||||
!selected_method->is_abstract()) {
|
||||
tty->print("default");
|
||||
}
|
||||
if (resolved_method->is_overpass()) {
|
||||
tty->print("overpass");
|
||||
}
|
||||
tty->cr();
|
||||
}
|
||||
// setup result
|
||||
result.set_virtual(resolved_klass, recv_klass, resolved_method, selected_method, vtable_index, CHECK);
|
||||
}
|
||||
|
@ -1042,6 +1143,17 @@ void LinkResolver::runtime_resolve_interface_method(CallInfo& result, methodHand
|
|||
THROW(vmSymbols::java_lang_NullPointerException());
|
||||
}
|
||||
|
||||
// check if private interface method
|
||||
if (resolved_klass->is_interface() && resolved_method->is_private()) {
|
||||
ResourceMark rm(THREAD);
|
||||
char buf[200];
|
||||
jio_snprintf(buf, sizeof(buf), "private interface method requires invokespecial, not invokeinterface: method %s",
|
||||
Method::name_and_sig_as_C_string(resolved_klass(),
|
||||
resolved_method->name(),
|
||||
resolved_method->signature()));
|
||||
THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf);
|
||||
}
|
||||
|
||||
// check if receiver klass implements the resolved interface
|
||||
if (!recv_klass->is_subtype_of(resolved_klass())) {
|
||||
ResourceMark rm(THREAD);
|
||||
|
@ -1071,20 +1183,7 @@ void LinkResolver::runtime_resolve_interface_method(CallInfo& result, methodHand
|
|||
resolved_method->signature()));
|
||||
}
|
||||
// check access
|
||||
if (sel_method->method_holder()->is_interface()) {
|
||||
// Method holder is an interface. Throw Illegal Access Error if sel_method
|
||||
// is neither public nor private.
|
||||
if (!(sel_method->is_public() || sel_method->is_private())) {
|
||||
ResourceMark rm(THREAD);
|
||||
THROW_MSG(vmSymbols::java_lang_IllegalAccessError(),
|
||||
Method::name_and_sig_as_C_string(recv_klass(),
|
||||
sel_method->name(),
|
||||
sel_method->signature()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Method holder is a class. Throw Illegal Access Error if sel_method
|
||||
// is not public.
|
||||
// Throw Illegal Access Error if sel_method is not public.
|
||||
if (!sel_method->is_public()) {
|
||||
ResourceMark rm(THREAD);
|
||||
THROW_MSG(vmSymbols::java_lang_IllegalAccessError(),
|
||||
|
@ -1092,7 +1191,7 @@ void LinkResolver::runtime_resolve_interface_method(CallInfo& result, methodHand
|
|||
sel_method->name(),
|
||||
sel_method->signature()));
|
||||
}
|
||||
}
|
||||
|
||||
// check if abstract
|
||||
if (check_null_and_abstract && sel_method->is_abstract()) {
|
||||
ResourceMark rm(THREAD);
|
||||
|
@ -1109,6 +1208,27 @@ void LinkResolver::runtime_resolve_interface_method(CallInfo& result, methodHand
|
|||
return;
|
||||
}
|
||||
int itable_index = resolved_method()->itable_index();
|
||||
|
||||
if (TraceItables && Verbose) {
|
||||
ResourceMark rm(THREAD);
|
||||
tty->print("invokeinterface selected method: receiver-class:%s, resolved-class:%s, method:%s, method_holder:%s, access_flags: ",
|
||||
(recv_klass.is_null() ? "<NULL>" : recv_klass->internal_name()),
|
||||
(resolved_klass.is_null() ? "<NULL>" : resolved_klass->internal_name()),
|
||||
Method::name_and_sig_as_C_string(resolved_klass(),
|
||||
resolved_method->name(),
|
||||
resolved_method->signature()),
|
||||
sel_method->method_holder()->internal_name()
|
||||
);
|
||||
sel_method->access_flags().print_on(tty);
|
||||
if (sel_method->method_holder()->is_interface() &&
|
||||
!sel_method->is_abstract()) {
|
||||
tty->print("default");
|
||||
}
|
||||
if (resolved_method->is_overpass()) {
|
||||
tty->print("overpass");
|
||||
}
|
||||
tty->cr();
|
||||
}
|
||||
result.set_interface(resolved_klass, recv_klass, resolved_method, sel_method, itable_index, CHECK);
|
||||
}
|
||||
|
||||
|
|
|
@ -1419,6 +1419,8 @@ Method* InstanceKlass::uncached_lookup_method(Symbol* name, Symbol* signature) c
|
|||
}
|
||||
|
||||
// lookup a method in all the interfaces that this class implements
|
||||
// Do NOT return private or static methods, new in JDK8 which are not externally visible
|
||||
// They should only be found in the initial InterfaceMethodRef
|
||||
Method* InstanceKlass::lookup_method_in_all_interfaces(Symbol* name,
|
||||
Symbol* signature) const {
|
||||
Array<Klass*>* all_ifs = transitive_interfaces();
|
||||
|
@ -1427,7 +1429,7 @@ Method* InstanceKlass::lookup_method_in_all_interfaces(Symbol* name,
|
|||
for (int i = 0; i < num_ifs; i++) {
|
||||
ik = InstanceKlass::cast(all_ifs->at(i));
|
||||
Method* m = ik->lookup_method(name, signature);
|
||||
if (m != NULL) {
|
||||
if (m != NULL && m->is_public() && !m->is_static()) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -292,9 +292,10 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar
|
|||
return allocate_new;
|
||||
}
|
||||
|
||||
// private methods 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
|
||||
// private methods not overriding
|
||||
// JDK8 adds private methods in interfaces which require invokespecial
|
||||
if (target_method()->is_private()) {
|
||||
return allocate_new;
|
||||
}
|
||||
|
@ -442,9 +443,10 @@ bool klassVtable::needs_new_vtable_entry(methodHandle target_method,
|
|||
return true;
|
||||
}
|
||||
|
||||
// private methods 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
|
||||
// private methods not overriding
|
||||
// JDK8 adds private methods in interfaces which require invokespecial
|
||||
if (target_method()->is_private()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -520,7 +522,7 @@ bool klassVtable::is_miranda_entry_at(int i) {
|
|||
Klass* method_holder = m->method_holder();
|
||||
InstanceKlass *mhk = InstanceKlass::cast(method_holder);
|
||||
|
||||
// miranda methods are interface methods in a class's vtable
|
||||
// miranda methods are public abstract instance interface methods in a class's vtable
|
||||
if (mhk->is_interface()) {
|
||||
assert(m->is_public(), "should be public");
|
||||
assert(ik()->implements_interface(method_holder) , "this class should implement the interface");
|
||||
|
@ -534,6 +536,8 @@ bool klassVtable::is_miranda_entry_at(int i) {
|
|||
// "miranda" means not static, not defined by this class, and not defined
|
||||
// in super unless it is private and therefore inaccessible to this class.
|
||||
// the caller must make sure that the method belongs to an interface implemented by the class
|
||||
// Miranda methods only include public interface instance methods
|
||||
// Not private methods, not static methods, not default = concrete abstract
|
||||
bool klassVtable::is_miranda(Method* m, Array<Method*>* class_methods, Klass* super) {
|
||||
if (m->is_static()) {
|
||||
return false;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue