8043275: Fix interface initialization for default methods
Initialize interfaces that declare concrete instance methods. Reviewed-by: kamg, coleenp, psandoz
This commit is contained in:
parent
209ffcd9a5
commit
0e1283a811
@ -2557,7 +2557,7 @@ methodHandle ClassFileParser::parse_method(bool is_interface,
|
||||
Array<Method*>* ClassFileParser::parse_methods(bool is_interface,
|
||||
AccessFlags* promoted_flags,
|
||||
bool* has_final_method,
|
||||
bool* has_default_methods,
|
||||
bool* declares_default_methods,
|
||||
TRAPS) {
|
||||
ClassFileStream* cfs = stream();
|
||||
cfs->guarantee_more(2, CHECK_NULL); // length
|
||||
@ -2576,11 +2576,11 @@ Array<Method*>* ClassFileParser::parse_methods(bool is_interface,
|
||||
if (method->is_final()) {
|
||||
*has_final_method = true;
|
||||
}
|
||||
if (is_interface && !(*has_default_methods)
|
||||
&& !method->is_abstract() && !method->is_static()
|
||||
&& !method->is_private()) {
|
||||
// default method
|
||||
*has_default_methods = true;
|
||||
// declares_default_methods: declares concrete instance methods, any access flags
|
||||
// used for interface initialization, and default method inheritance analysis
|
||||
if (is_interface && !(*declares_default_methods)
|
||||
&& !method->is_abstract() && !method->is_static()) {
|
||||
*declares_default_methods = true;
|
||||
}
|
||||
_methods->at_put(index, method());
|
||||
}
|
||||
@ -3739,6 +3739,7 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name,
|
||||
JvmtiCachedClassFileData *cached_class_file = NULL;
|
||||
Handle class_loader(THREAD, loader_data->class_loader());
|
||||
bool has_default_methods = false;
|
||||
bool declares_default_methods = false;
|
||||
ResourceMark rm(THREAD);
|
||||
|
||||
ClassFileStream* cfs = stream();
|
||||
@ -3976,9 +3977,13 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name,
|
||||
Array<Method*>* methods = parse_methods(access_flags.is_interface(),
|
||||
&promoted_flags,
|
||||
&has_final_method,
|
||||
&has_default_methods,
|
||||
&declares_default_methods,
|
||||
CHECK_(nullHandle));
|
||||
|
||||
if (declares_default_methods) {
|
||||
has_default_methods = true;
|
||||
}
|
||||
|
||||
// Additional attributes
|
||||
ClassAnnotationCollector parsed_annotations;
|
||||
parse_classfile_attributes(&parsed_annotations, CHECK_(nullHandle));
|
||||
@ -4120,6 +4125,7 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name,
|
||||
this_klass->set_minor_version(minor_version);
|
||||
this_klass->set_major_version(major_version);
|
||||
this_klass->set_has_default_methods(has_default_methods);
|
||||
this_klass->set_declares_default_methods(declares_default_methods);
|
||||
|
||||
if (!host_klass.is_null()) {
|
||||
assert (this_klass->is_anonymous(), "should be the same");
|
||||
|
5274
hotspot/src/share/vm/classfile/classFileParser.cpp.orig
Normal file
5274
hotspot/src/share/vm/classfile/classFileParser.cpp.orig
Normal file
File diff suppressed because it is too large
Load Diff
@ -247,7 +247,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
|
||||
Array<Method*>* parse_methods(bool is_interface,
|
||||
AccessFlags* promoted_flags,
|
||||
bool* has_final_method,
|
||||
bool* has_default_method,
|
||||
bool* declares_default_methods,
|
||||
TRAPS);
|
||||
intArray* sort_methods(Array<Method*>* methods);
|
||||
|
||||
|
@ -736,6 +736,41 @@ void InstanceKlass::link_methods(TRAPS) {
|
||||
}
|
||||
}
|
||||
|
||||
// Eagerly initialize superinterfaces that declare default methods (concrete instance: any access)
|
||||
void InstanceKlass::initialize_super_interfaces(instanceKlassHandle this_k, TRAPS) {
|
||||
if (this_k->has_default_methods()) {
|
||||
for (int i = 0; i < this_k->local_interfaces()->length(); ++i) {
|
||||
Klass* iface = this_k->local_interfaces()->at(i);
|
||||
InstanceKlass* ik = InstanceKlass::cast(iface);
|
||||
if (ik->should_be_initialized()) {
|
||||
if (ik->has_default_methods()) {
|
||||
ik->initialize_super_interfaces(ik, THREAD);
|
||||
}
|
||||
// Only initialize() interfaces that "declare" concrete methods.
|
||||
// has_default_methods drives searching superinterfaces since it
|
||||
// means has_default_methods in its superinterface hierarchy
|
||||
if (!HAS_PENDING_EXCEPTION && ik->declares_default_methods()) {
|
||||
ik->initialize(THREAD);
|
||||
}
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
Handle e(THREAD, PENDING_EXCEPTION);
|
||||
CLEAR_PENDING_EXCEPTION;
|
||||
{
|
||||
EXCEPTION_MARK;
|
||||
// Locks object, set state, and notify all waiting threads
|
||||
this_k->set_initialization_state_and_notify(
|
||||
initialization_error, THREAD);
|
||||
|
||||
// ignore any exception thrown, superclass initialization error is
|
||||
// thrown below
|
||||
CLEAR_PENDING_EXCEPTION;
|
||||
}
|
||||
THROW_OOP(e());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceKlass::initialize_impl(instanceKlassHandle this_k, TRAPS) {
|
||||
// Make sure klass is linked (verified) before initialization
|
||||
@ -815,33 +850,11 @@ void InstanceKlass::initialize_impl(instanceKlassHandle this_k, TRAPS) {
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively initialize any superinterfaces that declare default methods
|
||||
// Only need to recurse if has_default_methods which includes declaring and
|
||||
// inheriting default methods
|
||||
if (this_k->has_default_methods()) {
|
||||
// Step 7.5: initialize any interfaces which have default methods
|
||||
for (int i = 0; i < this_k->local_interfaces()->length(); ++i) {
|
||||
Klass* iface = this_k->local_interfaces()->at(i);
|
||||
InstanceKlass* ik = InstanceKlass::cast(iface);
|
||||
if (ik->has_default_methods() && ik->should_be_initialized()) {
|
||||
ik->initialize(THREAD);
|
||||
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
Handle e(THREAD, PENDING_EXCEPTION);
|
||||
CLEAR_PENDING_EXCEPTION;
|
||||
{
|
||||
EXCEPTION_MARK;
|
||||
// Locks object, set state, and notify all waiting threads
|
||||
this_k->set_initialization_state_and_notify(
|
||||
initialization_error, THREAD);
|
||||
|
||||
// ignore any exception thrown, superclass initialization error is
|
||||
// thrown below
|
||||
CLEAR_PENDING_EXCEPTION;
|
||||
}
|
||||
DTRACE_CLASSINIT_PROBE_WAIT(
|
||||
super__failed, InstanceKlass::cast(this_k()), -1, wait);
|
||||
THROW_OOP(e());
|
||||
}
|
||||
}
|
||||
}
|
||||
this_k->initialize_super_interfaces(this_k, CHECK);
|
||||
}
|
||||
|
||||
// Step 8
|
||||
|
@ -205,7 +205,8 @@ class InstanceKlass: public Klass {
|
||||
_misc_is_anonymous = 1 << 3, // has embedded _host_klass field
|
||||
_misc_is_contended = 1 << 4, // marked with contended annotation
|
||||
_misc_has_default_methods = 1 << 5, // class/superclass/implemented interfaces has default methods
|
||||
_misc_has_been_redefined = 1 << 6 // class has been redefined
|
||||
_misc_declares_default_methods = 1 << 6, // directly declares default methods (any access)
|
||||
_misc_has_been_redefined = 1 << 7 // class has been redefined
|
||||
};
|
||||
u2 _misc_flags;
|
||||
u2 _minor_version; // minor version number of class file
|
||||
@ -651,6 +652,17 @@ class InstanceKlass: public Klass {
|
||||
}
|
||||
}
|
||||
|
||||
bool declares_default_methods() const {
|
||||
return (_misc_flags & _misc_declares_default_methods) != 0;
|
||||
}
|
||||
void set_declares_default_methods(bool b) {
|
||||
if (b) {
|
||||
_misc_flags |= _misc_declares_default_methods;
|
||||
} else {
|
||||
_misc_flags &= ~_misc_declares_default_methods;
|
||||
}
|
||||
}
|
||||
|
||||
// for adding methods, ConstMethod::UNSET_IDNUM means no more ids available
|
||||
inline u2 next_method_idnum();
|
||||
void set_initial_method_idnum(u2 value) { _idnum_allocated_count = value; }
|
||||
@ -1022,6 +1034,7 @@ private:
|
||||
static bool link_class_impl (instanceKlassHandle this_k, bool throw_verifyerror, TRAPS);
|
||||
static bool verify_code (instanceKlassHandle this_k, bool throw_verifyerror, TRAPS);
|
||||
static void initialize_impl (instanceKlassHandle this_k, TRAPS);
|
||||
static void initialize_super_interfaces (instanceKlassHandle this_k, TRAPS);
|
||||
static void eager_initialize_impl (instanceKlassHandle this_k);
|
||||
static void set_initialization_state_and_notify_impl (instanceKlassHandle this_k, ClassState state, TRAPS);
|
||||
static void call_class_initializer_impl (instanceKlassHandle this_k, TRAPS);
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
/* This file contains dummy provider probes needed when compiling a hotspot
|
||||
* that does not support dtrace probes. This could be because we're building
|
||||
* on a system that doesn't suuport dtrace or because we're bulding a variant
|
||||
* on a system that doesn't support dtrace or because we're bulding a variant
|
||||
* of hotspot (like core) where we do not support dtrace
|
||||
*/
|
||||
#if !defined(DTRACE_ENABLED)
|
||||
|
@ -33,11 +33,12 @@
|
||||
import java.util.function.*;
|
||||
import java.util.*;
|
||||
|
||||
public class InvokespecialInterface {
|
||||
interface I {
|
||||
default void imethod() { System.out.println("I::imethod"); }
|
||||
}
|
||||
|
||||
class C implements I {
|
||||
static class C implements I {
|
||||
public void foo() { I.super.imethod(); } // invokespecial InterfaceMethod
|
||||
public void bar() { I i = this; i.imethod(); } // invokeinterface same
|
||||
public void doSomeInvokedynamic() {
|
||||
@ -48,7 +49,6 @@ class C implements I {
|
||||
}
|
||||
}
|
||||
|
||||
public class InvokespecialInterface {
|
||||
public static void main(java.lang.String[] unused) {
|
||||
// need to create C and call I::foo()
|
||||
C c = new C();
|
||||
|
87
hotspot/test/runtime/lambda-features/TestInterfaceInit.java
Normal file
87
hotspot/test/runtime/lambda-features/TestInterfaceInit.java
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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 8034275
|
||||
* @summary [JDK 8u40] Test interface initialization: only for interfaces declaring default methods
|
||||
* @run main TestInterfaceInit
|
||||
*/
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class TestInterfaceInit {
|
||||
|
||||
static List<Class<?>> cInitOrder = new ArrayList<>();
|
||||
|
||||
// Declares a default method and initializes
|
||||
interface I {
|
||||
boolean v = TestInterfaceInit.out(I.class);
|
||||
default void x() {}
|
||||
}
|
||||
|
||||
// Declares a default method and initializes
|
||||
interface J extends I {
|
||||
boolean v = TestInterfaceInit.out(J.class);
|
||||
default void x() {}
|
||||
}
|
||||
// No default method, does not initialize
|
||||
interface JN extends J {
|
||||
boolean v = TestInterfaceInit.out(JN.class);
|
||||
}
|
||||
|
||||
// Declares a default method and initializes
|
||||
interface K extends I {
|
||||
boolean v = TestInterfaceInit.out(K.class);
|
||||
default void x() {}
|
||||
}
|
||||
|
||||
// No default method, does not initialize
|
||||
interface KN extends K {
|
||||
boolean v = TestInterfaceInit.out(KN.class);
|
||||
}
|
||||
|
||||
interface L extends JN, KN {
|
||||
boolean v = TestInterfaceInit.out(L.class);
|
||||
default void x() {}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Trigger initialization
|
||||
boolean v = L.v;
|
||||
|
||||
List<Class<?>> expectedCInitOrder = Arrays.asList(I.class,J.class,K.class,L.class);
|
||||
if (!cInitOrder.equals(expectedCInitOrder)) {
|
||||
throw new RuntimeException(String.format("Class initialization array %s not equal to expected array %s", cInitOrder, expectedCInitOrder));
|
||||
}
|
||||
}
|
||||
|
||||
static boolean out(Class c) {
|
||||
System.out.println("#: initializing " + c.getName());
|
||||
cInitOrder.add(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
88
hotspot/test/runtime/lambda-features/TestInterfaceOrder.java
Normal file
88
hotspot/test/runtime/lambda-features/TestInterfaceOrder.java
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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 8034275
|
||||
* @summary [JDK 8u40] Test interface initialization order
|
||||
* @run main TestInterfaceOrder
|
||||
*/
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class TestInterfaceOrder {
|
||||
static List<Class<?>> cInitOrder = new ArrayList<>();
|
||||
|
||||
public static void main(java.lang.String[] args) {
|
||||
//Trigger initialization
|
||||
C c = new C();
|
||||
|
||||
List<Class<?>> expectedCInitOrder = Arrays.asList(I.class, J.class, A.class, K.class, B.class, L.class, C.class);
|
||||
if (!cInitOrder.equals(expectedCInitOrder)) {
|
||||
throw new RuntimeException(String.format("Class initialization order %s not equal to expected order %s", cInitOrder, expectedCInitOrder));
|
||||
}
|
||||
}
|
||||
|
||||
interface I {
|
||||
boolean v = TestInterfaceOrder.out(I.class);
|
||||
default void i() {}
|
||||
}
|
||||
|
||||
interface J extends I {
|
||||
boolean v = TestInterfaceOrder.out(J.class);
|
||||
default void j() {}
|
||||
}
|
||||
|
||||
static class A implements J {
|
||||
static boolean v = TestInterfaceOrder.out(A.class);
|
||||
}
|
||||
|
||||
interface K extends I {
|
||||
boolean v = TestInterfaceOrder.out(K.class);
|
||||
default void k() {}
|
||||
}
|
||||
|
||||
static class B extends A implements K {
|
||||
static boolean v = TestInterfaceOrder.out(B.class);
|
||||
}
|
||||
|
||||
interface L {
|
||||
boolean v = TestInterfaceOrder.out(L.class);
|
||||
default void l() {}
|
||||
}
|
||||
|
||||
static class C extends B implements L {
|
||||
static boolean v = TestInterfaceOrder.out(C.class);
|
||||
}
|
||||
|
||||
|
||||
static boolean out(Class c) {
|
||||
System.out.println("#: initializing " + c.getName());
|
||||
cInitOrder.add(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user