mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
8009130: Lambda: Fix access controls, loader constraints
New default methods list with inherited superinterface methods Reviewed-by: minqi, sspitsyn, coleenp
This commit is contained in:
parent
2b82651ec0
commit
088ded71f9
18 changed files with 662 additions and 248 deletions
|
@ -345,7 +345,6 @@ class MethodFamily : public ResourceObj {
|
|||
}
|
||||
|
||||
Symbol* generate_no_defaults_message(TRAPS) const;
|
||||
Symbol* generate_abstract_method_message(Method* method, TRAPS) const;
|
||||
Symbol* generate_conflicts_message(GrowableArray<Method*>* methods, TRAPS) const;
|
||||
|
||||
public:
|
||||
|
@ -404,20 +403,19 @@ class MethodFamily : public ResourceObj {
|
|||
_exception_message = generate_no_defaults_message(CHECK);
|
||||
_exception_name = vmSymbols::java_lang_AbstractMethodError();
|
||||
} else if (qualified_methods.length() == 1) {
|
||||
// leave abstract methods alone, they will be found via normal search path
|
||||
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 {
|
||||
if (!method->is_abstract()) {
|
||||
_selected_target = qualified_methods.at(0);
|
||||
}
|
||||
} else {
|
||||
_exception_message = generate_conflicts_message(&qualified_methods,CHECK);
|
||||
_exception_name = vmSymbols::java_lang_IncompatibleClassChangeError();
|
||||
if (TraceDefaultMethods) {
|
||||
_exception_message->print_value_on(tty);
|
||||
tty->print_cr("");
|
||||
}
|
||||
}
|
||||
|
||||
assert((has_target() ^ throws_exception()) == 1,
|
||||
"One and only one must be true");
|
||||
}
|
||||
|
||||
bool contains_signature(Symbol* query) {
|
||||
|
@ -475,20 +473,6 @@ Symbol* MethodFamily::generate_no_defaults_message(TRAPS) const {
|
|||
return SymbolTable::new_symbol("No qualifying defaults found", CHECK_NULL);
|
||||
}
|
||||
|
||||
Symbol* MethodFamily::generate_abstract_method_message(Method* method, TRAPS) const {
|
||||
Symbol* klass = method->klass_name();
|
||||
Symbol* name = method->name();
|
||||
Symbol* sig = method->signature();
|
||||
stringStream ss;
|
||||
ss.print("Method ");
|
||||
ss.write((const char*)klass->bytes(), klass->utf8_length());
|
||||
ss.print(".");
|
||||
ss.write((const char*)name->bytes(), name->utf8_length());
|
||||
ss.write((const char*)sig->bytes(), sig->utf8_length());
|
||||
ss.print(" is abstract");
|
||||
return SymbolTable::new_symbol(ss.base(), (int)ss.size(), CHECK_NULL);
|
||||
}
|
||||
|
||||
Symbol* MethodFamily::generate_conflicts_message(GrowableArray<Method*>* methods, TRAPS) const {
|
||||
stringStream ss;
|
||||
ss.print("Conflicting default methods:");
|
||||
|
@ -595,6 +579,18 @@ class EmptyVtableSlot : public ResourceObj {
|
|||
#endif // ndef PRODUCT
|
||||
};
|
||||
|
||||
static bool already_in_vtable_slots(GrowableArray<EmptyVtableSlot*>* slots, Method* m) {
|
||||
bool found = false;
|
||||
for (int j = 0; j < slots->length(); ++j) {
|
||||
if (slots->at(j)->name() == m->name() &&
|
||||
slots->at(j)->signature() == m->signature() ) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
static GrowableArray<EmptyVtableSlot*>* find_empty_vtable_slots(
|
||||
InstanceKlass* klass, GrowableArray<Method*>* mirandas, TRAPS) {
|
||||
|
||||
|
@ -604,8 +600,10 @@ static GrowableArray<EmptyVtableSlot*>* find_empty_vtable_slots(
|
|||
|
||||
// All miranda methods are obvious candidates
|
||||
for (int i = 0; i < mirandas->length(); ++i) {
|
||||
EmptyVtableSlot* slot = new EmptyVtableSlot(mirandas->at(i));
|
||||
slots->append(slot);
|
||||
Method* m = mirandas->at(i);
|
||||
if (!already_in_vtable_slots(slots, m)) {
|
||||
slots->append(new EmptyVtableSlot(m));
|
||||
}
|
||||
}
|
||||
|
||||
// Also any overpasses in our superclasses, that we haven't implemented.
|
||||
|
@ -621,7 +619,26 @@ static GrowableArray<EmptyVtableSlot*>* find_empty_vtable_slots(
|
|||
// unless we have a real implementation of it in the current class.
|
||||
Method* impl = klass->lookup_method(m->name(), m->signature());
|
||||
if (impl == NULL || impl->is_overpass()) {
|
||||
slots->append(new EmptyVtableSlot(m));
|
||||
if (!already_in_vtable_slots(slots, m)) {
|
||||
slots->append(new EmptyVtableSlot(m));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// also any default methods in our superclasses
|
||||
if (super->default_methods() != NULL) {
|
||||
for (int i = 0; i < super->default_methods()->length(); ++i) {
|
||||
Method* m = super->default_methods()->at(i);
|
||||
// m is a method that would have been a miranda if not for the
|
||||
// default method processing that occurred on behalf of our superclass,
|
||||
// so it's a method we want to re-examine in this new context. That is,
|
||||
// unless we have a real implementation of it in the current class.
|
||||
Method* impl = klass->lookup_method(m->name(), m->signature());
|
||||
if (impl == NULL || impl->is_overpass()) {
|
||||
if (!already_in_vtable_slots(slots, m)) {
|
||||
slots->append(new EmptyVtableSlot(m));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -679,7 +696,7 @@ class FindMethodsByErasedSig : public HierarchyVisitor<FindMethodsByErasedSig> {
|
|||
// 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 (m != NULL && !m->is_static() && (!iklass->is_interface() || m->is_public())) {
|
||||
if (_family == NULL) {
|
||||
_family = new StatefulMethodFamily();
|
||||
}
|
||||
|
@ -700,7 +717,7 @@ class FindMethodsByErasedSig : public HierarchyVisitor<FindMethodsByErasedSig> {
|
|||
|
||||
|
||||
|
||||
static void create_overpasses(
|
||||
static void create_defaults_and_exceptions(
|
||||
GrowableArray<EmptyVtableSlot*>* slots, InstanceKlass* klass, TRAPS);
|
||||
|
||||
static void generate_erased_defaults(
|
||||
|
@ -721,6 +738,8 @@ static void generate_erased_defaults(
|
|||
|
||||
static void merge_in_new_methods(InstanceKlass* klass,
|
||||
GrowableArray<Method*>* new_methods, TRAPS);
|
||||
static void create_default_methods( InstanceKlass* klass,
|
||||
GrowableArray<Method*>* new_methods, TRAPS);
|
||||
|
||||
// This is the guts of the default methods implementation. This is called just
|
||||
// after the classfile has been parsed if some ancestor has default methods.
|
||||
|
@ -782,7 +801,7 @@ void DefaultMethods::generate_default_methods(
|
|||
}
|
||||
#endif // ndef PRODUCT
|
||||
|
||||
create_overpasses(empty_slots, klass, CHECK);
|
||||
create_defaults_and_exceptions(empty_slots, klass, CHECK);
|
||||
|
||||
#ifndef PRODUCT
|
||||
if (TraceDefaultMethods) {
|
||||
|
@ -791,66 +810,6 @@ void DefaultMethods::generate_default_methods(
|
|||
#endif // ndef PRODUCT
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef ASSERT
|
||||
// Return true is broad type is a covariant return of narrow type
|
||||
static bool covariant_return_type(BasicType narrow, BasicType broad) {
|
||||
if (narrow == broad) {
|
||||
return true;
|
||||
}
|
||||
if (broad == T_OBJECT) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int assemble_redirect(
|
||||
BytecodeConstantPool* cp, BytecodeBuffer* buffer,
|
||||
Symbol* incoming, Method* target, TRAPS) {
|
||||
|
||||
BytecodeAssembler assem(buffer, cp);
|
||||
|
||||
SignatureStream in(incoming, true);
|
||||
SignatureStream out(target->signature(), true);
|
||||
u2 parameter_count = 0;
|
||||
|
||||
assem.aload(parameter_count++); // load 'this'
|
||||
|
||||
while (!in.at_return_type()) {
|
||||
assert(!out.at_return_type(), "Parameter counts do not match");
|
||||
BasicType bt = in.type();
|
||||
assert(out.type() == bt, "Parameter types are not compatible");
|
||||
assem.load(bt, parameter_count);
|
||||
if (in.is_object() && in.as_symbol(THREAD) != out.as_symbol(THREAD)) {
|
||||
assem.checkcast(out.as_symbol(THREAD));
|
||||
} else if (bt == T_LONG || bt == T_DOUBLE) {
|
||||
++parameter_count; // longs and doubles use two slots
|
||||
}
|
||||
++parameter_count;
|
||||
in.next();
|
||||
out.next();
|
||||
}
|
||||
assert(out.at_return_type(), "Parameter counts do not match");
|
||||
assert(covariant_return_type(out.type(), in.type()), "Return types are not compatible");
|
||||
|
||||
if (parameter_count == 1 && (in.type() == T_LONG || in.type() == T_DOUBLE)) {
|
||||
++parameter_count; // need room for return value
|
||||
}
|
||||
if (target->method_holder()->is_interface()) {
|
||||
assem.invokespecial(target);
|
||||
} else {
|
||||
assem.invokevirtual(target);
|
||||
}
|
||||
|
||||
if (in.is_object() && in.as_symbol(THREAD) != out.as_symbol(THREAD)) {
|
||||
assem.checkcast(in.as_symbol(THREAD));
|
||||
}
|
||||
assem._return(in.type());
|
||||
return parameter_count;
|
||||
}
|
||||
|
||||
static int assemble_method_error(
|
||||
BytecodeConstantPool* cp, BytecodeBuffer* buffer, Symbol* errorName, Symbol* message, TRAPS) {
|
||||
|
||||
|
@ -924,18 +883,18 @@ static void switchover_constant_pool(BytecodeConstantPool* bpool,
|
|||
}
|
||||
}
|
||||
|
||||
// A "bridge" is a method created by javac to bridge the gap between
|
||||
// an implementation and a generically-compatible, but different, signature.
|
||||
// Bridges have actual bytecode implementation in classfiles.
|
||||
// An "overpass", on the other hand, performs the same function as a bridge
|
||||
// but does not occur in a classfile; the VM creates overpass itself,
|
||||
// when it needs a path to get from a call site to an default method, and
|
||||
// a bridge doesn't exist.
|
||||
static void create_overpasses(
|
||||
// Create default_methods list for the current class.
|
||||
// With the VM only processing erased signatures, the VM only
|
||||
// creates an overpass in a conflict case or a case with no candidates.
|
||||
// This allows virtual methods to override the overpass, but ensures
|
||||
// that a local method search will find the exception rather than an abstract
|
||||
// or default method that is not a valid candidate.
|
||||
static void create_defaults_and_exceptions(
|
||||
GrowableArray<EmptyVtableSlot*>* slots,
|
||||
InstanceKlass* klass, TRAPS) {
|
||||
|
||||
GrowableArray<Method*> overpasses;
|
||||
GrowableArray<Method*> defaults;
|
||||
BytecodeConstantPool bpool(klass->constants());
|
||||
|
||||
for (int i = 0; i < slots->length(); ++i) {
|
||||
|
@ -943,7 +902,6 @@ static void create_overpasses(
|
|||
|
||||
if (slot->is_bound()) {
|
||||
MethodFamily* method = slot->get_binding();
|
||||
int max_stack = 0;
|
||||
BytecodeBuffer buffer;
|
||||
|
||||
#ifndef PRODUCT
|
||||
|
@ -953,26 +911,27 @@ static void create_overpasses(
|
|||
tty->print_cr("");
|
||||
if (method->has_target()) {
|
||||
method->print_selected(tty, 1);
|
||||
} else {
|
||||
} else if (method->throws_exception()) {
|
||||
method->print_exception(tty, 1);
|
||||
}
|
||||
}
|
||||
#endif // ndef PRODUCT
|
||||
|
||||
if (method->has_target()) {
|
||||
Method* selected = method->get_selected_target();
|
||||
if (selected->method_holder()->is_interface()) {
|
||||
max_stack = assemble_redirect(
|
||||
&bpool, &buffer, slot->signature(), selected, CHECK);
|
||||
defaults.push(selected);
|
||||
}
|
||||
} else if (method->throws_exception()) {
|
||||
max_stack = assemble_method_error(&bpool, &buffer, method->get_exception_name(), method->get_exception_message(), CHECK);
|
||||
}
|
||||
if (max_stack != 0) {
|
||||
int max_stack = assemble_method_error(&bpool, &buffer,
|
||||
method->get_exception_name(), method->get_exception_message(), CHECK);
|
||||
AccessFlags flags = accessFlags_from(
|
||||
JVM_ACC_PUBLIC | JVM_ACC_SYNTHETIC | JVM_ACC_BRIDGE);
|
||||
Method* m = new_method(&bpool, &buffer, slot->name(), slot->signature(),
|
||||
Method* m = new_method(&bpool, &buffer, slot->name(), slot->signature(),
|
||||
flags, max_stack, slot->size_of_parameters(),
|
||||
ConstMethod::OVERPASS, CHECK);
|
||||
// We push to the methods list:
|
||||
// overpass methods which are exception throwing methods
|
||||
if (m != NULL) {
|
||||
overpasses.push(m);
|
||||
}
|
||||
|
@ -983,11 +942,31 @@ static void create_overpasses(
|
|||
#ifndef PRODUCT
|
||||
if (TraceDefaultMethods) {
|
||||
tty->print_cr("Created %d overpass methods", overpasses.length());
|
||||
tty->print_cr("Created %d default methods", defaults.length());
|
||||
}
|
||||
#endif // ndef PRODUCT
|
||||
|
||||
switchover_constant_pool(&bpool, klass, &overpasses, CHECK);
|
||||
merge_in_new_methods(klass, &overpasses, CHECK);
|
||||
if (overpasses.length() > 0) {
|
||||
switchover_constant_pool(&bpool, klass, &overpasses, CHECK);
|
||||
merge_in_new_methods(klass, &overpasses, CHECK);
|
||||
}
|
||||
if (defaults.length() > 0) {
|
||||
create_default_methods(klass, &defaults, CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
static void create_default_methods( InstanceKlass* klass,
|
||||
GrowableArray<Method*>* new_methods, TRAPS) {
|
||||
|
||||
int new_size = new_methods->length();
|
||||
Array<Method*>* total_default_methods = MetadataFactory::new_array<Method*>(
|
||||
klass->class_loader_data(), new_size, NULL, CHECK);
|
||||
for (int index = 0; index < new_size; index++ ) {
|
||||
total_default_methods->at_put(index, new_methods->at(index));
|
||||
}
|
||||
Method::sort_methods(total_default_methods, false, false);
|
||||
|
||||
klass->set_default_methods(total_default_methods);
|
||||
}
|
||||
|
||||
static void sort_methods(GrowableArray<Method*>* methods) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue