1 /*
   2  * Copyright (c) 2013, 2017, 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  * @test
  26  * @summary static initializer invocation order
  27  * @library /lib/testlibrary/bytecode /java/lang/invoke/common
  28  * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
  29  * @run main test.java.lang.invoke.CallStaticInitOrder
  30  */
  31 
  32 package test.java.lang.invoke;
  33 
  34 import java.lang.invoke.*;
  35 
  36 import static java.lang.invoke.MethodHandles.*;
  37 import static java.lang.invoke.MethodType.*;
  38 import static test.java.lang.invoke.lib.InstructionHelper.invokedynamic;
  39 
  40 public class CallStaticInitOrder {
  41     private static int TICK;
  42     private static synchronized int tick(String event) {
  43         int n = ++TICK;
  44         System.out.println("event #"+n+" = "+event);
  45         return n;
  46     }
  47 
  48     static int Init1Tick;
  49     private static class Init1 {
  50         static { Init1Tick = tick("foo -> Init1.<clinit>"); }
  51         static int foo() { return Init1Tick; }
  52     }
  53 
  54     static int Init2Tick;
  55     private static class Init2 {
  56         static { Init2Tick = tick("bar -> Init2.<clinit>"); }
  57         static int bar() { return Init2Tick; }
  58     }
  59 
  60     static int Init3Tick;
  61     private static class Init3 {
  62         static { Init3Tick = tick("baz -> Init3.<clinit>"); }
  63         static int baz() { return Init3Tick; }
  64     }
  65 
  66     static int Init4Tick;
  67     private static class Init4 {
  68         static { Init4Tick = tick("bat -> Init4.<clinit>"); }
  69         static int bat() { return Init4Tick; }
  70     }
  71 
  72     static int Init5Tick;
  73     private static class Init5 {
  74         static { Init5Tick = tick("read bang -> Init5.<clinit>"); }
  75         static int bang = Init5Tick;
  76     }
  77 
  78     static int Init6Tick;
  79     private static class Init6 {
  80         static { Init6Tick = tick("write pong -> Init6.<clinit>"); }
  81         static int pong;
  82     }
  83 
  84     private static final MutableCallSite CONSTANT_CS_baz;
  85     private static MethodHandle MH_foo() throws ReflectiveOperationException {
  86         return lookup().findStatic(Init1.class, "foo", methodType(int.class));
  87     }
  88     private static final MethodHandle CONSTANT_MH_bar;
  89     private static MethodHandle MH_baz() throws ReflectiveOperationException {
  90         return lookup().findStatic(Init3.class, "baz", methodType(int.class));
  91     }
  92     private static final MethodHandle CONSTANT_MH_bat;
  93     private static final MethodHandle CONSTANT_MH_bangGetter;
  94     private static final MethodHandle CONSTANT_MH_pongSetter;
  95 
  96     private static final MethodHandle INDY_foo;
  97     private static final MethodHandle INDY_bar;
  98     private static final MethodHandle INDY_baz;
  99     private static final MethodHandle INDY_bat;
 100     private static final MethodHandle INDY_bang;
 101     private static final MethodHandle INDY_pong;
 102     static {
 103         try {
 104             int t1 = tick("CallStaticInitOrder.<clinit>");
 105             {
 106                 MethodHandles.Lookup l = lookup();
 107                 CONSTANT_CS_baz = new MutableCallSite(methodType(int.class));
 108                 // MH_foo() := lookup().findStatic(Init1.class, "foo", methodType(int.class));
 109                 CONSTANT_MH_bar = l.findStatic(Init2.class, "bar", methodType(int.class));
 110                 // MH_baz() := lookup().findStatic(Init3.class, "baz", methodType(int.class));
 111                 CONSTANT_MH_bat = l.unreflect(Init4.class.getDeclaredMethod("bat"));
 112                 CONSTANT_MH_bangGetter = l.findStaticGetter(Init5.class, "bang", int.class);
 113                 MethodHandle pongSetter = l.findStaticSetter(Init6.class, "pong", int.class);
 114                 MethodHandle tickGetter = l.findStaticGetter(CallStaticInitOrder.class, "Init6Tick", int.class);
 115                 CONSTANT_MH_pongSetter = filterReturnValue(insertArguments(pongSetter, 0, -99), tickGetter);
 116 
 117                 MethodType bsmType = methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
 118                 INDY_foo = invokedynamic(l, "foo", methodType(int.class), "bsm", bsmType, S -> {});
 119                 INDY_bar = invokedynamic(l, "bar", methodType(int.class), "bsm", bsmType, S -> {});
 120                 INDY_baz = invokedynamic(l, "baz", methodType(int.class), "bsm", bsmType, S -> {});
 121                 INDY_bat = invokedynamic(l, "bat", methodType(int.class), "bsm", bsmType, S -> {});
 122                 INDY_bang = invokedynamic(l, "bang", methodType(int.class), "bsm", bsmType, S -> {});
 123                 INDY_pong = invokedynamic(l, "pong", methodType(int.class), "bsm", bsmType, S -> {});
 124             }
 125             int t2 = tick("CallStaticInitOrder.<clinit> done");
 126             assertEquals(t1+1, t2);  // no ticks in between
 127         } catch (Exception ex) {
 128             throw new InternalError(ex.toString());
 129         }
 130     }
 131 
 132     public static void main(String... av) throws Throwable {
 133         testInit();
 134         if (LAST_LOSER != null)  throw LAST_LOSER;
 135     }
 136 
 137     private static Throwable LAST_LOSER;
 138 
 139     private static void assertEquals(int expected, int actual) {
 140         if (expected != actual) {
 141             Throwable loser = new AssertionError("expected: " + expected + ", actual: " + actual);
 142             if (LAST_LOSER != null)
 143                 LAST_LOSER.printStackTrace(System.out);
 144             LAST_LOSER = loser;
 145         }
 146     }
 147 
 148     private static void testInit() throws Throwable {
 149         System.out.println("runFoo = "+runFoo());
 150         System.out.println("runBar = "+runBar());
 151         try {
 152             runBaz();
 153         } catch (IllegalStateException ex) {
 154             tick("runBaz throw/catch");
 155         }
 156         CONSTANT_CS_baz.setTarget(MH_baz());
 157         System.out.println("runBaz = "+runBaz());
 158         System.out.println("runBat = "+runBat());
 159         System.out.println("runBang = "+runBang());
 160         System.out.println("runPong = "+runPong());
 161     }
 162 
 163     private static int runFoo() throws Throwable {
 164         assertEquals(Init1Tick, 0);  // Init1 not initialized yet
 165         int t1 = tick("runFoo");
 166         int t2 = (int) INDY_foo.invokeExact();
 167         int t3 = tick("runFoo done");
 168         assertEquals(Init1Tick, t2);  // when Init1 was initialized
 169         assertEquals(t1+2, t3);  // exactly two ticks in between
 170         assertEquals(t1+1, t2);  // init happened inside
 171         return t2;
 172     }
 173 
 174     private static int runBar() throws Throwable {
 175         assertEquals(Init2Tick, 0);  // Init2 not initialized yet
 176         int t1 = tick("runBar");
 177         int t2 = (int) INDY_bar.invokeExact();
 178         int t3 = tick("runBar done");
 179         assertEquals(Init2Tick, t2);  // when Init2 was initialized
 180         assertEquals(t1+2, t3);  // exactly two ticks in between
 181         assertEquals(t1+1, t2);  // init happened inside
 182         return t2;
 183     }
 184 
 185     private static int runBaz() throws Throwable {
 186         assertEquals(Init3Tick, 0);  // Init3 not initialized yet
 187         int t1 = tick("runBaz");
 188         int t2 = (int) INDY_baz.invokeExact();
 189         int t3 = tick("runBaz done");
 190         assertEquals(Init3Tick, t2);  // when Init3 was initialized
 191         assertEquals(t1+2, t3);  // exactly two ticks in between
 192         assertEquals(t1+1, t2);  // init happened inside
 193         return t2;
 194     }
 195 
 196     private static int runBat() throws Throwable {
 197         assertEquals(Init4Tick, 0);  // Init4 not initialized yet
 198         int t1 = tick("runBat");
 199         int t2 = (int) INDY_bat.invokeExact();
 200         int t3 = tick("runBat done");
 201         assertEquals(Init4Tick, t2);  // when Init4 was initialized
 202         assertEquals(t1+2, t3);  // exactly two ticks in between
 203         assertEquals(t1+1, t2);  // init happened inside
 204         return t2;
 205     }
 206 
 207     private static int runBang() throws Throwable {
 208         assertEquals(Init5Tick, 0);  // Init5 not initialized yet
 209         int t1 = tick("runBang");
 210         int t2 = (int) INDY_bang.invokeExact();
 211         int t3 = tick("runBang done");
 212         assertEquals(Init5Tick, t2);  // when Init5 was initialized
 213         assertEquals(t1+2, t3);  // exactly two ticks in between
 214         assertEquals(t1+1, t2);  // init happened inside
 215         return t2;
 216     }
 217 
 218     private static int runPong() throws Throwable {
 219         assertEquals(Init6Tick, 0);  // Init6 not initialized yet
 220         int t1 = tick("runPong");
 221         int t2 = (int) INDY_pong.invokeExact();
 222         int t3 = tick("runPong done");
 223         assertEquals(Init6Tick, t2);  // when Init6 was initialized
 224         assertEquals(t1+2, t3);  // exactly two ticks in between
 225         assertEquals(t1+1, t2);  // init happened inside
 226         return t2;
 227     }
 228 
 229     static CallSite bsm(Lookup caller, String name, MethodType type) throws ReflectiveOperationException {
 230         System.out.println("bsm "+name+type);
 231         CallSite res;
 232         switch (name) {
 233         case "foo":
 234             res = new ConstantCallSite(MH_foo()); break;
 235         case "bar":
 236             res = new ConstantCallSite(CONSTANT_MH_bar); break;
 237         case "baz":
 238             res = CONSTANT_CS_baz; break;
 239         case "bat":
 240             res = new ConstantCallSite(CONSTANT_MH_bat); break;
 241         case "bang":
 242             res = new ConstantCallSite(CONSTANT_MH_bangGetter); break;
 243         case "pong":
 244             res = new ConstantCallSite(CONSTANT_MH_pongSetter); break;
 245         default:
 246             res = null;
 247         }
 248         if (res == null || !res.type().equals(type)) {
 249             throw new AssertionError(String.valueOf(res));
 250         }
 251         return res;
 252     }
 253 }