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