hotspot/test/runtime/lambda-features/InterfaceInitializationStates.java
changeset 41293 871b2f487dc0
child 42031 55dc92f033b9
equal deleted inserted replaced
41291:e9a1638b8cea 41293:871b2f487dc0
       
     1 /*
       
     2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  *
       
    23  */
       
    24 
       
    25 /*
       
    26  * @test
       
    27  * @bug 8163969
       
    28  * @summary Test interface initialization states and when certain interfaces are initialized
       
    29  * in the presence of initialization errors.
       
    30  * @run main InterfaceInitializationStates
       
    31  */
       
    32 
       
    33 import java.util.List;
       
    34 import java.util.Arrays;
       
    35 import java.util.ArrayList;
       
    36 
       
    37 public class InterfaceInitializationStates {
       
    38 
       
    39     static List<Class<?>> cInitOrder = new ArrayList<>();
       
    40 
       
    41     // K interface with a default method has an initialization error
       
    42     interface K {
       
    43         boolean v = InterfaceInitializationStates.out(K.class);
       
    44         static final Object CONST = InterfaceInitializationStates.someMethod();
       
    45         default int method() { return 2; }
       
    46     }
       
    47 
       
    48     // I is initialized when CONST is used, and doesn't trigger initialization of K,
       
    49     // I also doesn't get an initialization error just because K has an initialization error.
       
    50     interface I extends K {
       
    51         boolean v = InterfaceInitializationStates.out(I.class);
       
    52         static final Object CONST = InterfaceInitializationStates.someMethod();
       
    53     }
       
    54 
       
    55     // L can be fully initialized even though it extends an interface that has an
       
    56     // initialization error
       
    57     interface L extends K {
       
    58         boolean v = InterfaceInitializationStates.out(L.class);
       
    59         default void lx() {}
       
    60         static void func() {
       
    61             System.out.println("Calling function on interface with bad super interface.");
       
    62         }
       
    63     }
       
    64 
       
    65     // Another interface needing initialization.
       
    66     // Initialization of this interface does not occur with ClassLIM because K throws
       
    67     // an initialization error, so the interface initialization is abandoned
       
    68     interface M {
       
    69         boolean v = InterfaceInitializationStates.out(M.class);
       
    70         default void mx() {}
       
    71     }
       
    72 
       
    73     static class ClassLIM implements L, I, M {
       
    74         boolean v = InterfaceInitializationStates.out(ClassLIM.class);
       
    75         int callMethodInK() { return method(); }
       
    76         static {
       
    77             // Since interface initialization of K fails, this should never be called
       
    78             System.out.println("Initializing C, but L is still good");
       
    79             L.func();
       
    80         }
       
    81     }
       
    82 
       
    83     // Finally initialize M
       
    84     static class ClassM implements M {
       
    85         boolean v = InterfaceInitializationStates.out(ClassM.class);
       
    86     }
       
    87 
       
    88     // Iunlinked is testing initialization like interface I, except interface I is linked when
       
    89     // ClassLIM is linked.
       
    90     // Iunlinked is not linked already when K gets an initialization error.  Linking Iunlinked
       
    91     // should succeed and not get NoClassDefFoundError because it does not depend on the
       
    92     // initialization state of K for linking.  There's bug now where it gets this error.
       
    93     // See: https://bugs.openjdk.java.net/browse/JDK-8166203.
       
    94     interface Iunlinked extends K {
       
    95         boolean v = InterfaceInitializationStates.out(Iunlinked.class);
       
    96     }
       
    97 
       
    98     // More tests.  What happens if we use K for parameters and return types?
       
    99     // K is a symbolic reference in the constant pool and the initialization error only
       
   100     // matters when it's used.
       
   101     interface Iparams {
       
   102         boolean v = InterfaceInitializationStates.out(Iparams.class);
       
   103         K the_k = null;
       
   104         K m(K k); // abstract
       
   105         default K method() { return new K(){}; }
       
   106     }
       
   107 
       
   108     static class ClassIparams implements Iparams {
       
   109         boolean v = InterfaceInitializationStates.out(ClassIparams.class);
       
   110         public K m(K k) { return k; }
       
   111     }
       
   112 
       
   113     public static void main(java.lang.String[] unused) {
       
   114         // The rule this tests is the last sentence of JLS 12.4.1:
       
   115 
       
   116         // When a class is initialized, its superclasses are initialized (if they have not
       
   117         // been previously initialized), as well as any superinterfaces (s8.1.5) that declare any
       
   118         // default methods (s9.4.3) (if they have not been previously initialized). Initialization
       
   119         // of an interface does not, of itself, cause initialization of any of its superinterfaces.
       
   120 
       
   121         // Trigger initialization.
       
   122         // Now L is fully_initialized even though K should
       
   123         // throw an error during initialization.
       
   124         boolean v = L.v;
       
   125         L.func();
       
   126 
       
   127         try {
       
   128             ClassLIM c  = new ClassLIM();  // is K initialized, with a perfectly good L in the middle
       
   129             // was bug: this used to succeed and be able to callMethodInK().
       
   130             throw new RuntimeException("FAIL exception not thrown for class");
       
   131         } catch (ExceptionInInitializerError e) {
       
   132             System.out.println("ExceptionInInitializerError thrown as expected");
       
   133         }
       
   134 
       
   135         // Test that K already has initialization error so gets ClassNotFoundException because
       
   136         // initialization was attempted with ClassLIM.
       
   137         try {
       
   138             Class.forName("InterfaceInitializationStates$K", true, InterfaceInitializationStates.class.getClassLoader());
       
   139             throw new RuntimeException("FAIL exception not thrown for forName(K)");
       
   140         } catch(ClassNotFoundException e) {
       
   141             throw new RuntimeException("ClassNotFoundException should not be thrown");
       
   142         } catch(NoClassDefFoundError e) {
       
   143             System.out.println("NoClassDefFoundError thrown as expected");
       
   144         }
       
   145 
       
   146         new ClassM();
       
   147 
       
   148         // Initialize I, which doesn't cause K (super interface) to be initialized.
       
   149         // Since the initialization of I does _not_ cause K to be initialized, it does
       
   150         // not get NoClassDefFoundError because K is erroneous.
       
   151         // But the initialization of I throws RuntimeException, so we expect
       
   152         // ExceptionInInitializerError.
       
   153         try {
       
   154             Object ii = I.CONST;
       
   155             throw new RuntimeException("FAIL exception not thrown for I's initialization");
       
   156         } catch (ExceptionInInitializerError e) {
       
   157             System.out.println("ExceptionInInitializerError as expected");
       
   158         }
       
   159 
       
   160         // Initialize Iunlinked. This should not get NoClassDefFoundError because K
       
   161         // (its super interface) is in initialization_error state.
       
   162         // This is a bug.  It does now.
       
   163         try {
       
   164             boolean bb = Iunlinked.v;
       
   165             throw new RuntimeException("FAIL exception not thrown for Iunlinked initialization");
       
   166         } catch(NoClassDefFoundError e) {
       
   167             System.out.println("NoClassDefFoundError thrown because of bug");
       
   168         }
       
   169 
       
   170         // This should be okay
       
   171         boolean value = Iparams.v;
       
   172         System.out.println("value is " + value);
       
   173 
       
   174         ClassIparams p = new ClassIparams();
       
   175         try {
       
   176             // Now we get an error because K got an initialization_error
       
   177             K kk = p.method();
       
   178             throw new RuntimeException("FAIL exception not thrown for calling method for K");
       
   179         } catch(NoClassDefFoundError e) {
       
   180             System.out.println("NoClassDefFoundError thrown as expected");
       
   181         }
       
   182 
       
   183          // Check expected class initialization order
       
   184         List<Class<?>> expectedCInitOrder = Arrays.asList(L.class, K.class, M.class, ClassM.class,
       
   185                                                           I.class, Iparams.class,
       
   186                                                           ClassIparams.class);
       
   187         if (!cInitOrder.equals(expectedCInitOrder)) {
       
   188             throw new RuntimeException(
       
   189                 String.format("Class initialization array %s not equal to expected array %s",
       
   190                               cInitOrder, expectedCInitOrder));
       
   191         }
       
   192     }
       
   193 
       
   194     static boolean out(Class c) {
       
   195         System.out.println("#: initializing " + c.getName());
       
   196         cInitOrder.add(c);
       
   197         return true;
       
   198     }
       
   199     static Object someMethod() {
       
   200         throw new RuntimeException();
       
   201     }
       
   202 }