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