8043275: Fix interface initialization for default methods

Initialize interfaces that declare concrete instance methods.

Reviewed-by: kamg, coleenp, psandoz
This commit is contained in:
Karen Kinnear 2014-10-22 15:24:37 -07:00
parent 209ffcd9a5
commit 0e1283a811
9 changed files with 5525 additions and 44 deletions

View File

@ -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");

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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

View File

@ -199,13 +199,14 @@ class InstanceKlass: public Klass {
bool _has_unloaded_dependent;
enum {
_misc_rewritten = 1 << 0, // methods rewritten.
_misc_has_nonstatic_fields = 1 << 1, // for sizing with UseCompressedOops
_misc_should_verify_class = 1 << 2, // allow caching of preverification
_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_rewritten = 1 << 0, // methods rewritten.
_misc_has_nonstatic_fields = 1 << 1, // for sizing with UseCompressedOops
_misc_should_verify_class = 1 << 2, // allow caching of preverification
_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_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);

View File

@ -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)

View File

@ -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();

View 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;
}
}

View 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;
}
}