1 /* 2 * Copyright (c) 2015, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* @test 27 * @bug 8139885 28 * @bug 8150635 29 * @bug 8150956 30 * @bug 8150957 31 * @bug 8151179 32 * @bug 8152667 33 * @bug 8153637 34 * @bug 8154751 35 * @bug 8154754 36 * @bug 8167974 37 * @run testng/othervm -ea -esa test.java.lang.invoke.LoopCombinatorTest 38 */ 39 40 package test.java.lang.invoke; 41 42 import java.lang.invoke.MethodHandle; 43 import java.lang.invoke.MethodHandles; 44 import java.lang.invoke.MethodHandles.Lookup; 45 import java.lang.invoke.MethodType; 46 import java.util.*; 47 48 import static java.lang.invoke.MethodType.methodType; 49 50 import static org.testng.AssertJUnit.*; 51 52 import org.testng.annotations.*; 53 54 /** 55 * Tests for the loop combinators introduced in JEP 274. 56 */ 57 public class LoopCombinatorTest { 58 59 static final Lookup LOOKUP = MethodHandles.lookup(); 60 61 @Test testLoopFac()62 public static void testLoopFac() throws Throwable { 63 MethodHandle[] counterClause = new MethodHandle[]{Fac.MH_zero, Fac.MH_inc}; 64 MethodHandle[] accumulatorClause = new MethodHandle[]{Fac.MH_one, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin}; 65 MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause); 66 assertEquals(Fac.MT_fac, loop.type()); 67 assertEquals(120, loop.invoke(5)); 68 } 69 70 @Test testLoopFacNullInit()71 public static void testLoopFacNullInit() throws Throwable { 72 // null initializer for counter, should initialize to 0 73 MethodHandle[] counterClause = new MethodHandle[]{null, Fac.MH_inc}; 74 MethodHandle[] accumulatorClause = new MethodHandle[]{Fac.MH_one, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin}; 75 MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause); 76 assertEquals(Fac.MT_fac, loop.type()); 77 assertEquals(120, loop.invoke(5)); 78 } 79 80 @Test testLoopNullInit()81 public static void testLoopNullInit() throws Throwable { 82 // null initializer for counter, should initialize to 0, one-clause loop 83 MethodHandle[] counterClause = new MethodHandle[]{null, Loop.MH_inc, Loop.MH_pred, Loop.MH_fin}; 84 MethodHandle loop = MethodHandles.loop(counterClause); 85 assertEquals(Loop.MT_loop, loop.type()); 86 assertEquals(10, loop.invoke(10)); 87 } 88 89 @Test testLoopVoid1()90 public static void testLoopVoid1() throws Throwable { 91 // construct a post-checked loop that only does one iteration and has a void body and void local state 92 MethodHandle loop = MethodHandles.loop(new MethodHandle[]{Empty.MH_f, Empty.MH_f, Empty.MH_pred, null}); 93 assertEquals(MethodType.methodType(void.class), loop.type()); 94 loop.invoke(); 95 } 96 97 @Test testLoopVoid2()98 public static void testLoopVoid2() throws Throwable { 99 // construct a post-checked loop that only does one iteration and has a void body and void local state, 100 // initialized implicitly from the step type 101 MethodHandle loop = MethodHandles.loop(new MethodHandle[]{null, Empty.MH_f, Empty.MH_pred, null}); 102 assertEquals(MethodType.methodType(void.class), loop.type()); 103 loop.invoke(); 104 } 105 106 @Test testLoopVoid3()107 public static void testLoopVoid3() throws Throwable { 108 // construct a post-checked loop that only does one iteration and has a void body and void local state, 109 // and that has a void finalizer 110 MethodHandle loop = MethodHandles.loop(new MethodHandle[]{null, Empty.MH_f, Empty.MH_pred, Empty.MH_f}); 111 assertEquals(MethodType.methodType(void.class), loop.type()); 112 loop.invoke(); 113 } 114 115 @Test testLoopFacWithVoidState()116 public static void testLoopFacWithVoidState() throws Throwable { 117 // like testLoopFac, but with additional void state that outputs a dot 118 MethodHandle[] counterClause = new MethodHandle[]{Fac.MH_zero, Fac.MH_inc}; 119 MethodHandle[] accumulatorClause = new MethodHandle[]{Fac.MH_one, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin}; 120 MethodHandle[] dotClause = new MethodHandle[]{null, Fac.MH_dot}; 121 MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause, dotClause); 122 assertEquals(Fac.MT_fac, loop.type()); 123 assertEquals(120, loop.invoke(5)); 124 } 125 126 @Test testLoopVoidInt()127 public static void testLoopVoidInt() throws Throwable { 128 // construct a post-checked loop that only does one iteration and has a void body and void local state, 129 // and that returns a constant 130 MethodHandle loop = MethodHandles.loop(new MethodHandle[]{null, Empty.MH_f, Empty.MH_pred, Empty.MH_c}); 131 assertEquals(MethodType.methodType(int.class), loop.type()); 132 assertEquals(23, loop.invoke()); 133 } 134 135 @Test testLoopWithVirtuals()136 public static void testLoopWithVirtuals() throws Throwable { 137 // construct a loop (to calculate factorial) that uses a mix of static and virtual methods 138 MethodHandle[] counterClause = new MethodHandle[]{null, LoopWithVirtuals.permute(LoopWithVirtuals.MH_inc)}; 139 MethodHandle[] accumulatorClause = new MethodHandle[]{ 140 // init function must indicate the loop arguments (there is no other means to determine them) 141 MethodHandles.dropArguments(LoopWithVirtuals.MH_one, 0, LoopWithVirtuals.class), 142 LoopWithVirtuals.permute(LoopWithVirtuals.MH_mult), 143 LoopWithVirtuals.permute(LoopWithVirtuals.MH_pred), 144 LoopWithVirtuals.permute(LoopWithVirtuals.MH_fin) 145 }; 146 MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause); 147 assertEquals(LoopWithVirtuals.MT_loop, loop.type()); 148 assertEquals(120, loop.invoke(new LoopWithVirtuals(), 5)); 149 } 150 151 @Test testLoopOmitPred()152 public static void testLoopOmitPred() throws Throwable { 153 // construct a loop to calculate factorial that omits a predicate 154 MethodHandle[] counterClause = new MethodHandle[]{null, Fac.MH_inc, null, Fac.MH_fin}; 155 MethodHandle[] accumulatorClause = new MethodHandle[]{Fac.MH_one, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin}; 156 MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause); 157 assertEquals(Fac.MT_fac, loop.type()); 158 assertEquals(120, loop.invoke(5)); 159 } 160 161 @DataProvider negativeTestData()162 static Object[][] negativeTestData() { 163 MethodHandle i0 = MethodHandles.constant(int.class, 0); 164 MethodHandle ii = MethodHandles.dropArguments(i0, 0, int.class, int.class); 165 MethodHandle id = MethodHandles.dropArguments(i0, 0, int.class, double.class); 166 MethodHandle i3 = MethodHandles.dropArguments(i0, 0, int.class, int.class, int.class); 167 List<MethodHandle> inits = Arrays.asList(ii, id, i3); 168 List<Class<?>> ints3 = Arrays.asList(int.class, int.class, int.class); 169 List<Class<?>> ints4 = Arrays.asList(int.class, int.class, int.class, int.class); 170 List<MethodHandle> finis = Arrays.asList(Fac.MH_fin, Fac.MH_inc, Counted.MH_step); 171 List<MethodHandle> preds1 = Arrays.asList(null, null, null); 172 List<MethodHandle> preds2 = Arrays.asList(null, Fac.MH_fin, null); 173 MethodHandle eek = MethodHandles.dropArguments(i0, 0, int.class, int.class, double.class); 174 List<MethodHandle> nesteps = Arrays.asList(Fac.MH_inc, eek, Fac.MH_dot); 175 List<MethodHandle> nepreds = Arrays.asList(null, Fac.MH_pred, null); 176 List<MethodHandle> nefinis = Arrays.asList(null, Fac.MH_fin, null); 177 List<MethodHandle> lvsteps = Arrays.asList(LoopWithVirtuals.MH_inc, LoopWithVirtuals.MH_mult); 178 List<MethodHandle> lvpreds = Arrays.asList(null, LoopWithVirtuals.MH_pred); 179 List<MethodHandle> lvfinis = Arrays.asList(null, LoopWithVirtuals.MH_fin); 180 return new Object[][] { 181 {null, "null or no clauses passed"}, 182 {new MethodHandle[][]{}, "null or no clauses passed"}, 183 {new MethodHandle[][]{{null, Fac.MH_inc}, {Fac.MH_one, null, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin}}, 184 "All loop clauses must be represented as MethodHandle arrays with at most 4 elements."}, 185 {new MethodHandle[][]{{null, Fac.MH_inc}, null}, "null clauses are not allowed"}, 186 {new MethodHandle[][]{{Fac.MH_zero, Fac.MH_dot}}, 187 "clause 0: init and step return types must match: int != void"}, 188 {new MethodHandle[][]{{ii}, {id}, {i3}}, 189 "found non-effectively identical init parameter type lists: " + inits + 190 " (common suffix: " + ints3 + ")"}, 191 {new MethodHandle[][]{{null, Fac.MH_inc, null, Fac.MH_fin}, {null, Fac.MH_inc, null, Fac.MH_inc}, 192 {null, Counted.MH_start, null, Counted.MH_step}}, 193 "found non-identical finalizer return types: " + finis + " (return type: int)"}, 194 {new MethodHandle[][]{{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, Fac.MH_mult, null, Fac.MH_fin}, 195 {null, Fac.MH_dot}}, "no predicate found: " + preds1}, 196 {new MethodHandle[][]{{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, Fac.MH_mult, Fac.MH_fin, Fac.MH_fin}, 197 {null, Fac.MH_dot}}, "predicates must have boolean return type: " + preds2}, 198 {new MethodHandle[][]{{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, eek, Fac.MH_pred, Fac.MH_fin}, 199 {null, Fac.MH_dot}}, 200 "found non-effectively identical parameter type lists:\nstep: " + nesteps + 201 "\npred: " + nepreds + "\nfini: " + nefinis + " (common parameter sequence: " + ints3 + ")"}, 202 {new MethodHandle[][]{{null, LoopWithVirtuals.MH_inc}, 203 {LoopWithVirtuals.MH_one, LoopWithVirtuals.MH_mult, LoopWithVirtuals.MH_pred, LoopWithVirtuals.MH_fin}}, 204 "found non-effectively identical parameter type lists:\nstep: " + lvsteps + 205 "\npred: " + lvpreds + "\nfini: " + lvfinis + " (common parameter sequence: " + ints4 + ")"} 206 }; 207 } 208 209 static final MethodHandle MH_loop; 210 211 static { 212 try { 213 MH_loop = LOOKUP.findStatic(MethodHandles.class, "loop", methodType(MethodHandle.class, MethodHandle[][].class)); 214 } catch (NoSuchMethodException | IllegalAccessException e) { 215 throw new ExceptionInInitializerError(e); 216 } 217 } 218 219 @Test(dataProvider = "negativeTestData") testLoopNegative(MethodHandle[][] clauses, String expectedMessage)220 public static void testLoopNegative(MethodHandle[][] clauses, String expectedMessage) throws Throwable { 221 boolean caught = false; 222 try { 223 MH_loop.invokeWithArguments((Object[]) clauses); 224 } catch (IllegalArgumentException iae) { 225 assertEquals(expectedMessage, iae.getMessage()); 226 caught = true; 227 } 228 assertTrue(caught); 229 } 230 231 @Test(dataProvider = "whileLoopTestData") testWhileLoop(MethodHandle MH_zero, MethodHandle MH_pred, MethodHandle MH_step, String messageOrNull)232 public static void testWhileLoop(MethodHandle MH_zero, 233 MethodHandle MH_pred, 234 MethodHandle MH_step, 235 String messageOrNull) throws Throwable { 236 // int i = 0; while (i < limit) { ++i; } return i; => limit 237 try { 238 MethodHandle loop = MethodHandles.whileLoop(MH_zero, MH_pred, MH_step); 239 assert messageOrNull == null; 240 if (MH_step.type().equals(While.MH_step.type())) 241 assertEquals(While.MT_while, loop.type()); 242 assertEquals(MH_step.type().dropParameterTypes(0, 1), loop.type()); 243 while (loop.type().parameterCount() > 1) loop = snip(loop); 244 assertEquals(23, loop.invoke(23)); 245 } catch (IllegalArgumentException iae) { 246 assert messageOrNull != null; 247 assertEqualsFIXME(messageOrNull, iae.getMessage()); 248 } 249 } 250 assertEqualsFIXME(String expect, String actual)251 static void assertEqualsFIXME(String expect, String actual) { 252 if (!expect.equals(actual)) { 253 // just issue a warning 254 System.out.println("*** "+actual+"\n != "+expect); 255 } 256 } 257 258 @DataProvider whileLoopTestData()259 static Object[][] whileLoopTestData() { 260 MethodHandle 261 zeroI = While.MH_zero, 262 zeroX = snip(zeroI), 263 zeroIB = slap(zeroI, byte.class), 264 predII = While.MH_pred, 265 predIX = snip(predII), 266 predIIB = slap(predII, byte.class), 267 stepII = While.MH_step, 268 stepIX = snip(stepII), 269 stepIIB = slap(stepII, byte.class) 270 ; 271 return new Object[][] { 272 // normal while loop clauses, perhaps with effectively-identical reductions 273 {zeroI, predII, stepII, null}, 274 {zeroX, predII, stepII, null}, 275 {null, predII, stepII, null}, 276 // expanded while loop clauses 277 {zeroIB, predIIB, stepIIB, null}, 278 {zeroI, predIIB, stepIIB, null}, 279 {null, predIIB, stepIIB, null}, 280 {zeroIB, predII, stepIIB, null}, 281 {zeroX, predII, stepIIB, null}, 282 {null, predII, stepIIB, null}, 283 // short step clauses cause errors 284 {zeroI, predII, stepIX, "loop predicate must match: (int,int)boolean != (int)boolean"}, 285 {zeroIB, predIX, stepIX, "loop initializer must match: (int,byte)int != ()int"}, 286 // bad body type 287 {zeroI, predII, tweak(stepII, -1, char.class), "body function must match: (int,int)char != (char,int,int)char"}, 288 {zeroI, predII, tweak(stepII, 0, char.class), "body function must match: (char,int)int != (int,char,int)int"}, 289 // bad pred type 290 {zeroI, tweak(predII, -1, char.class), stepII, "loop predicate must match: (int,int)char != (int,int)boolean"}, 291 {zeroI, tweak(predII, 0, char.class), stepII, "loop predicate must match: (char,int)boolean != (int,int)boolean"}, 292 // bad init type 293 {tweak(zeroI, -1, char.class), predII, stepII, "loop initializer must match: (int)char != (int)int"}, 294 {tweak(zeroI, 0, char.class), predII, stepII, "loop initializer must match: (char)int != (int)int"}, 295 }; 296 } 297 298 // tweak the type of an MH tweak(MethodHandle mh, int argPos, Class<?> type)299 static MethodHandle tweak(MethodHandle mh, int argPos, Class<?> type) { 300 MethodType mt = mh.type(); 301 if (argPos == -1) 302 mt = mt.changeReturnType(type); 303 else 304 mt = mt.changeParameterType(argPos, type); 305 return MethodHandles.explicitCastArguments(mh, mt); 306 } 307 // snip off an MH argument, hard-wiring to zero snip(MethodHandle mh, int argPos)308 static MethodHandle snip(MethodHandle mh, int argPos) { 309 if (argPos < 0) return null; // special case for optional args 310 Class<?> argType = mh.type().parameterType(argPos); 311 Object zero; 312 try { 313 zero = MethodHandles.zero(argType).invoke(); 314 } catch (Throwable ex) { 315 throw new AssertionError(ex); 316 } 317 return MethodHandles.insertArguments(mh, argPos, zero); 318 } snip(MethodHandle mh)319 static MethodHandle snip(MethodHandle mh) { 320 return snip(mh, mh.type().parameterCount()-1); 321 } 322 // slap on an extra type on the end of the MH slap(MethodHandle mh, Class<?> addType)323 static MethodHandle slap(MethodHandle mh, Class<?> addType) { 324 return MethodHandles.dropArguments(mh, mh.type().parameterCount(), addType); 325 } 326 327 @Test testWhileLoopNoIteration()328 public static void testWhileLoopNoIteration() throws Throwable { 329 // a while loop that never executes its body because the predicate evaluates to false immediately 330 MethodHandle loop = MethodHandles.whileLoop(While.MH_initString, While.MH_predString, While.MH_stepString); 331 assertEquals(While.MT_string, loop.type()); 332 assertEquals("a", loop.invoke()); 333 } 334 335 @Test(dataProvider = "whileLoopTestData") testDoWhileLoop(MethodHandle MH_zero, MethodHandle MH_pred, MethodHandle MH_step, String messageOrNull)336 public static void testDoWhileLoop(MethodHandle MH_zero, 337 MethodHandle MH_pred, 338 MethodHandle MH_step, 339 String messageOrNull) throws Throwable { 340 // int i = 0; do { ++i; } while (i < limit); return i; => limit 341 try { 342 MethodHandle loop = MethodHandles.doWhileLoop(MH_zero, MH_step, MH_pred); 343 assert messageOrNull == null; 344 if (MH_step.type().equals(While.MH_step.type())) 345 assertEquals(While.MT_while, loop.type()); 346 assertEquals(MH_step.type().dropParameterTypes(0, 1), loop.type()); 347 while (loop.type().parameterCount() > 1) loop = snip(loop); 348 assertEquals(23, loop.invoke(23)); 349 } catch (IllegalArgumentException iae) { 350 assert messageOrNull != null; 351 if (!messageOrNull.equals(iae.getMessage())) { 352 // just issue a warning 353 System.out.println("*** "+messageOrNull+"\n != "+iae.getMessage()); 354 } 355 } 356 } 357 358 @Test testDoWhileBadInit()359 public static void testDoWhileBadInit() throws Throwable { 360 boolean caught = false; 361 try { 362 While w = new While(); 363 MethodHandle loop = MethodHandles.doWhileLoop(MethodHandles.empty(methodType(char.class)), 364 While.MH_voidBody.bindTo(w), 365 While.MH_voidPred.bindTo(w)); 366 } catch (IllegalArgumentException iae) { 367 assertEquals("loop initializer must match: ()char != (int)void", iae.getMessage()); 368 caught = true; 369 } 370 assertTrue(caught); 371 } 372 373 @Test testWhileZip()374 public static void testWhileZip() throws Throwable { 375 MethodHandle loop = MethodHandles.doWhileLoop(While.MH_zipInitZip, While.MH_zipStep, While.MH_zipPred); 376 assertEquals(While.MT_zip, loop.type()); 377 List<String> a = Arrays.asList("a", "b", "c", "d"); 378 List<String> b = Arrays.asList("e", "f", "g", "h"); 379 List<String> zipped = Arrays.asList("a", "e", "b", "f", "c", "g", "d", "h"); 380 assertEquals(zipped, (List<String>) loop.invoke(a.iterator(), b.iterator())); 381 } 382 383 @Test testWhileBadInit()384 public static void testWhileBadInit() throws Throwable { 385 boolean caught = false; 386 try { 387 While w = new While(); 388 MethodHandle loop = MethodHandles.whileLoop(MethodHandles.empty(methodType(void.class, char.class)), 389 While.MH_voidPred.bindTo(w), 390 While.MH_voidBody.bindTo(w)); 391 } catch (IllegalArgumentException iae) { 392 assertEquals("loop initializer must match: (char)void != (int)void", iae.getMessage()); 393 caught = true; 394 } 395 assertTrue(caught); 396 } 397 398 @Test testWhileVoidInit()399 public static void testWhileVoidInit() throws Throwable { 400 While w = new While(); 401 int v = 5; 402 MethodHandle loop = MethodHandles.whileLoop(While.MH_voidInit.bindTo(w), While.MH_voidPred.bindTo(w), 403 While.MH_voidBody.bindTo(w)); 404 assertEquals(While.MT_void, loop.type()); 405 loop.invoke(v); 406 assertEquals(v, w.i); 407 } 408 409 @Test testDoWhileVoidInit()410 public static void testDoWhileVoidInit() throws Throwable { 411 While w = new While(); 412 int v = 5; 413 MethodHandle loop = MethodHandles.doWhileLoop(While.MH_voidInit.bindTo(w), While.MH_voidBody.bindTo(w), 414 While.MH_voidPred.bindTo(w)); 415 assertEquals(While.MT_void, loop.type()); 416 loop.invoke(v); 417 assertEquals(v, w.i); 418 } 419 420 @DataProvider nullArgs()421 static Object[][] nullArgs() { 422 MethodHandle c = MethodHandles.constant(int.class, 1); 423 return new Object[][]{{null, c}, {c, null}}; 424 } 425 426 @Test(dataProvider = "nullArgs", expectedExceptions = NullPointerException.class) testWhileNullArgs(MethodHandle pred, MethodHandle body)427 public static void testWhileNullArgs(MethodHandle pred, MethodHandle body) { 428 MethodHandles.whileLoop(null, pred, body); 429 } 430 431 @Test(dataProvider = "nullArgs", expectedExceptions = NullPointerException.class) testDoWhileNullArgs(MethodHandle body, MethodHandle pred)432 public static void testDoWhileNullArgs(MethodHandle body, MethodHandle pred) { 433 MethodHandles.whileLoop(null, body, pred); 434 } 435 436 @Test testCountedLoop()437 public static void testCountedLoop() throws Throwable { 438 // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s; => a variation on a well known theme 439 MethodHandle fit13 = MethodHandles.dropArguments(MethodHandles.constant(int.class, 13), 0, String.class); 440 MethodHandle loop = MethodHandles.countedLoop(fit13, Counted.MH_start, Counted.MH_step); 441 assertEquals(Counted.MT_counted, loop.type()); 442 assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!")); 443 } 444 445 @Test testCountedLoopVoidInit()446 public static void testCountedLoopVoidInit() throws Throwable { 447 MethodHandle fit5 = MethodHandles.constant(int.class, 5); 448 for (int i = 0; i < 8; i++) { 449 MethodHandle zero = MethodHandles.zero(void.class); 450 MethodHandle init = fit5; 451 MethodHandle body = Counted.MH_printHello; 452 boolean useNull = (i & 1) != 0, addInitArg = (i & 2) != 0, addBodyArg = (i & 4) != 0; 453 if (useNull) zero = null; 454 if (addInitArg) init = MethodHandles.dropArguments(init, 0, int.class); 455 if (addBodyArg) body = MethodHandles.dropArguments(body, 1, int.class); 456 System.out.println("testCountedLoopVoidInit i="+i+" : "+Arrays.asList(init, zero, body)); 457 MethodHandle loop = MethodHandles.countedLoop(init, zero, body); 458 MethodType expectedType = Counted.MT_countedPrinting; 459 if (addInitArg || addBodyArg) 460 expectedType = expectedType.insertParameterTypes(0, int.class); 461 assertEquals(expectedType, loop.type()); 462 if (addInitArg || addBodyArg) 463 loop.invoke(99); 464 else 465 loop.invoke(); 466 } 467 } 468 469 @Test testCountedArrayLoop()470 public static void testCountedArrayLoop() throws Throwable { 471 // int[] a = new int[]{0}; for (int i = 0; i < 13; ++i) { ++a[0]; } => a[0] == 13 472 MethodHandle fit13 = MethodHandles.dropArguments(MethodHandles.constant(int.class, 13), 0, int[].class); 473 MethodHandle loop = MethodHandles.countedLoop(fit13, null, Counted.MH_stepUpdateArray); 474 assertEquals(Counted.MT_arrayCounted, loop.type()); 475 int[] a = new int[]{0}; 476 loop.invoke(a); 477 assertEquals(13, a[0]); 478 } 479 480 @Test testCountedPrintingLoop()481 public static void testCountedPrintingLoop() throws Throwable { 482 MethodHandle fit5 = MethodHandles.constant(int.class, 5); 483 MethodHandle loop = MethodHandles.countedLoop(fit5, null, Counted.MH_printHello); 484 assertEquals(Counted.MT_countedPrinting, loop.type()); 485 loop.invoke(); 486 } 487 488 @Test(expectedExceptions = NullPointerException.class) testCountedLoopNullBody()489 public static void testCountedLoopNullBody() throws Throwable { 490 MethodHandle h5 = MethodHandles.constant(int.class, 5); 491 MethodHandle h13 = MethodHandles.constant(int.class, 13); 492 MethodHandle loop = MethodHandles.countedLoop(h5, h13, null); 493 assertEquals(methodType(int.class), loop.type()); 494 assertEquals(13, loop.invoke()); 495 } 496 497 @Test(expectedExceptions = NullPointerException.class) testCountedLoopNullIterations()498 public static void testCountedLoopNullIterations() throws Throwable { 499 MethodHandle loop = MethodHandles.countedLoop(null, null, null); 500 assertEquals(methodType(void.class), loop.type()); 501 loop.invoke(); 502 } 503 504 @Test(expectedExceptions = NullPointerException.class) testCountedLoopNullInitAndBody()505 public static void testCountedLoopNullInitAndBody() throws Throwable { 506 MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5), null, null); 507 assertEquals(methodType(void.class), loop.type()); 508 loop.invoke(); 509 } 510 511 @DataProvider countedLoopBodyParameters()512 static Object[][] countedLoopBodyParameters() { 513 Class<?> V = String.class, I = int.class, A = List.class; 514 // return types are of these forms: 515 // {count = int(A...), init = V(A...), body = V(V, I, A...)} 516 return new Object[][] { 517 // body leads determining A... 518 {methodType(I), methodType(V), methodType(V, V, I)}, 519 {methodType(I), methodType(V), methodType(V, V, I, A)}, 520 {methodType(I,A), methodType(V), methodType(V, V, I, A)}, 521 {methodType(I), methodType(V,A), methodType(V, V, I, A)}, 522 // body leads, with void V 523 {methodType(I), methodType(void.class), methodType(void.class, I)}, 524 {methodType(I), methodType(void.class), methodType(void.class, I, A)}, 525 {methodType(I,A), methodType(void.class), methodType(void.class, I, A)}, 526 {methodType(I), methodType(void.class,A), methodType(void.class, I, A)}, 527 // count leads determining A..., but only if body drops all A... 528 {methodType(I,A), methodType(V), methodType(V, V, I)}, 529 {methodType(I,A), methodType(V,A), methodType(V, V, I)}, 530 // count leads, with void V 531 {methodType(I,A), methodType(void.class), methodType(void.class, I)}, 532 {methodType(I,A), methodType(void.class,A), methodType(void.class, I)}, 533 }; 534 } 535 536 @Test(dataProvider = "countedLoopBodyParameters") testCountedLoopBodyParameters(MethodType countType, MethodType initType, MethodType bodyType)537 public static void testCountedLoopBodyParameters(MethodType countType, MethodType initType, MethodType bodyType) throws Throwable { 538 MethodHandle loop = MethodHandles.countedLoop( 539 MethodHandles.empty(countType), 540 initType == null ? null : MethodHandles.empty(initType), 541 MethodHandles.empty(bodyType)); 542 // The rule: If body takes the minimum number of parameters, then take what countType offers. 543 // The initType has to just roll with whatever the other two agree on. 544 int innerParams = (bodyType.returnType() == void.class ? 1 : 2); 545 MethodType expectType = bodyType.dropParameterTypes(0, innerParams); 546 if (expectType.parameterCount() == 0) 547 expectType = expectType.insertParameterTypes(0, countType.parameterList()); 548 assertEquals(expectType, loop.type()); 549 } 550 551 @Test(dataProvider = "countedLoopBodyParameters") testCountedLoopBodyParametersNullInit(MethodType countType, MethodType initType, MethodType bodyType)552 public static void testCountedLoopBodyParametersNullInit(MethodType countType, MethodType initType, MethodType bodyType) throws Throwable { 553 testCountedLoopBodyParameters(countType, null, bodyType); 554 } 555 556 @Test testCountedLoopStateInitializedToNull()557 public static void testCountedLoopStateInitializedToNull() throws Throwable { 558 MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5), 559 MethodHandles.empty(methodType(String.class)), Counted.MH_stateBody); 560 assertEquals(Counted.MT_bodyDeterminesState, loop.type()); 561 assertEquals("sssssnull01234", loop.invoke()); 562 } 563 564 @Test testCountedLoopArgsDefinedByIterations()565 public static void testCountedLoopArgsDefinedByIterations() throws Throwable { 566 MethodHandle iterations = 567 MethodHandles.dropArguments(MethodHandles.constant(int.class, 3), 0, String.class); 568 MethodHandle loop = MethodHandles.countedLoop(iterations, 569 MethodHandles.empty(iterations.type().changeReturnType(String.class)), Counted.MH_append); 570 assertEquals(Counted.MT_iterationsDefineArgs, loop.type()); 571 assertEquals("hello012", loop.invoke("hello")); 572 } 573 574 @Test testCountedRangeLoop()575 public static void testCountedRangeLoop() throws Throwable { 576 // String s = "Lambdaman!"; for (int i = -5; i < 8; ++i) { s = "na " + s; } return s; => a well known theme 577 MethodHandle fitm5 = MethodHandles.dropArguments(Counted.MH_m5, 0, String.class); 578 MethodHandle fit8 = MethodHandles.dropArguments(Counted.MH_8, 0, String.class); 579 MethodHandle loop = MethodHandles.countedLoop(fitm5, fit8, Counted.MH_start, Counted.MH_step); 580 assertEquals(Counted.MT_counted, loop.type()); 581 assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!")); 582 } 583 584 @Test testCountedLoopCounterInit()585 public static void testCountedLoopCounterInit() throws Throwable { 586 // int x = 0; for (int i = 0; i < 5; ++i) { x += i; } return x; => 10 587 // (only if counter's first value in body is 0) 588 MethodHandle iter = MethodHandles.constant(int.class, 5); 589 MethodHandle init = MethodHandles.constant(int.class, 0); 590 MethodHandle body = Counted.MH_addCounter; 591 MethodHandle loop = MethodHandles.countedLoop(iter, init, body); 592 assertEquals(Counted.MT_counterInit, loop.type()); 593 assertEquals(10, loop.invoke()); 594 } 595 596 @Test testCountedLoopEmpty()597 public static void testCountedLoopEmpty() throws Throwable { 598 // for (int i = 0; i < 5; ++i) { /* empty */ } 599 MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5), null, 600 MethodHandles.empty(methodType(void.class, int.class))); 601 assertEquals(methodType(void.class), loop.type()); 602 loop.invoke(); 603 } 604 605 @Test testCountedRangeLoopEmpty()606 public static void testCountedRangeLoopEmpty() throws Throwable { 607 // for (int i = -5; i < 5; ++i) { /* empty */ } 608 MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, -5), 609 MethodHandles.constant(int.class, 5), null, MethodHandles.empty(methodType(void.class, int.class))); 610 assertEquals(methodType(void.class), loop.type()); 611 loop.invoke(); 612 } 613 614 @DataProvider countedLoopNegativeData()615 static Object[][] countedLoopNegativeData() { 616 MethodHandle dummy = MethodHandles.zero(void.class); 617 MethodHandle one = MethodHandles.constant(int.class, 1); 618 MethodHandle oneString = MethodHandles.dropArguments(one, 0, String.class); 619 MethodHandle oneDouble = MethodHandles.dropArguments(one, 0, double.class); 620 return new Object[][]{ 621 {dummy, one, dummy, dummy, String.format("start/end must return int %s, %s", dummy, one)}, 622 {one, dummy, dummy, dummy, String.format("start/end must return int %s, %s", one, dummy)}, 623 {oneString, oneDouble, dummy, dummy, 624 String.format("start and end parameter types must match: %s != %s", oneString.type(), 625 oneDouble.type())}, 626 {oneString, oneString, dummy, dummy, 627 String.format("start/end and init parameter types must match: %s != %s", oneString.type(), 628 dummy.type())}, 629 {one, one, null, dummy, String.format("actual and expected body signatures must match: %s != %s", 630 dummy.type(), dummy.type().appendParameterTypes(int.class))} 631 }; 632 } 633 634 @Test(dataProvider = "countedLoopNegativeData") testCountedLoopNegative(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body, String msg)635 public static void testCountedLoopNegative(MethodHandle start, MethodHandle end, MethodHandle init, 636 MethodHandle body, String msg) { 637 if (true) return; //%%%FIXME%%%% 638 boolean caught = false; 639 try { 640 MethodHandles.countedLoop(start, end, init, body); 641 } catch (IllegalArgumentException iae) { 642 assertEquals(msg, iae.getMessage()); 643 caught = true; 644 } 645 assertTrue(caught); 646 } 647 648 @Test testIterateSum()649 public static void testIterateSum() throws Throwable { 650 // Integer[] a = new Integer[]{1,2,3,4,5,6}; int sum = 0; for (int e : a) { sum += e; } return sum; => 21 651 MethodHandle loop = MethodHandles.iteratedLoop(Iterate.MH_sumIterator, Iterate.MH_sumInit, Iterate.MH_sumStep); 652 assertEquals(Iterate.MT_sum, loop.type()); 653 assertEquals(21, loop.invoke(new Integer[]{1, 2, 3, 4, 5, 6})); 654 } 655 656 @DataProvider iteratorInits()657 static Object[][] iteratorInits() { 658 return new Object[][]{{Iterate.MH_iteratorFromList}, {Iterate.MH_iteratorFromIterable}, {null}}; 659 } 660 661 @Test(dataProvider = "iteratorInits") testIterateReverse(MethodHandle iterator)662 public static void testIterateReverse(MethodHandle iterator) throws Throwable { 663 // this test uses List as its loop state type; don't try to change that 664 if (iterator != null) 665 iterator = iterator.asType(iterator.type().changeParameterType(0, List.class)); 666 for (int i = 0; i < 4; i++) { 667 MethodHandle init = Iterate.MH_reverseInit, body = Iterate.MH_reverseStep; 668 boolean snipInit = (i & 1) != 0, snipBody = (i & 2) != 0; 669 if (snipInit) init = snip(init); 670 if (snipBody) body = snip(body); 671 if (!snipInit && snipBody && iterator == null) { 672 // Body does not determine (A...), so the default guy just picks Iterable. 673 // If body insisted on (List), the default guy would adjust himself. 674 // Init has no authority to change the (A...), so must patch init. 675 // All according to plan! 676 init = slap(snip(init), Iterable.class); 677 } 678 System.out.println("testIterateReverse i="+i+" : "+Arrays.asList(iterator, init, body)); 679 MethodHandle loop = MethodHandles.iteratedLoop(iterator, init, body); 680 MethodType expectedType = Iterate.MT_reverse; 681 if (iterator == null && i >= 2) 682 expectedType = expectedType.changeParameterType(0, Iterable.class); 683 assertEquals(expectedType, loop.type()); 684 List<String> list = Arrays.asList("a", "b", "c", "d", "e"); 685 List<String> reversedList = Arrays.asList("e", "d", "c", "b", "a"); 686 assertEquals(reversedList, (List<String>) loop.invoke(list)); 687 } 688 } 689 690 @Test(dataProvider = "iteratorInits") testIterateLength(MethodHandle iterator)691 public static void testIterateLength(MethodHandle iterator) throws Throwable { 692 MethodHandle body = Iterate.MH_lengthStep; 693 MethodHandle init = Iterate.MH_lengthInit; 694 MethodType expectedType = Iterate.MT_length; 695 int barity = body.type().parameterCount(); 696 Class<?> iteratorSource = iterator == null ? null : iterator.type().parameterType(0); 697 if (iterator != null && iteratorSource != body.type().parameterType(barity-1)) { 698 // adjust body to accept the other type 699 body = body.asType(body.type().changeParameterType(barity-1, iteratorSource)); 700 init = init.asType(init.type().changeParameterType(0, iteratorSource)); 701 expectedType = expectedType.changeParameterType(0, iteratorSource); 702 } 703 for (;; init = snip(init)) { 704 System.out.println("testIterateLength.init = "+init); 705 MethodHandle loop = MethodHandles.iteratedLoop(iterator, init, body); 706 assertEquals(expectedType, loop.type()); 707 List<Double> list = Arrays.asList(23.0, 148.0, 42.0); 708 assertEquals(list.size(), (int) loop.invoke(list)); 709 if (init == null) break; 710 } 711 } 712 713 @Test(dataProvider = "iteratorInits") testIterateMap(MethodHandle iterator)714 public static void testIterateMap(MethodHandle iterator) throws Throwable { 715 MethodHandle body = Iterate.MH_mapStep; 716 MethodHandle init = Iterate.MH_mapInit; 717 MethodType expectedType = Iterate.MT_map; 718 int barity = body.type().parameterCount(); 719 Class<?> iteratorSource = iterator == null ? null : iterator.type().parameterType(0); 720 if (iterator != null && iteratorSource != body.type().parameterType(barity-1)) { 721 // adjust body to accept the other type 722 body = body.asType(body.type().changeParameterType(barity-1, iteratorSource)); 723 init = init.asType(init.type().changeParameterType(0, iteratorSource)); 724 expectedType = expectedType.changeParameterType(0, iteratorSource); 725 } 726 for (; init != null; init = snip(init)) { 727 System.out.println("testIterateMap.init = "+init); 728 MethodHandle loop = MethodHandles.iteratedLoop(iterator, init, body); 729 assertEquals(expectedType, loop.type()); 730 List<String> list = Arrays.asList("Hello", "world", "!"); 731 List<String> upList = Arrays.asList("HELLO", "WORLD", "!"); 732 assertEquals(upList, (List<String>) loop.invoke(list)); 733 } 734 } 735 736 @Test(dataProvider = "iteratorInits") testIteratePrint(MethodHandle iterator)737 public static void testIteratePrint(MethodHandle iterator) throws Throwable { 738 MethodHandle body = Iterate.MH_printStep; 739 MethodType expectedType = Iterate.MT_print; 740 int barity = body.type().parameterCount(); 741 Class<?> iteratorSource = iterator == null ? null : iterator.type().parameterType(0); 742 if (iterator != null && iteratorSource != body.type().parameterType(barity-1)) { 743 // adjust body to accept the other type 744 body = body.asType(body.type().changeParameterType(barity-1, iteratorSource)); 745 expectedType = expectedType.changeParameterType(0, iteratorSource); 746 } 747 MethodHandle loop = MethodHandles.iteratedLoop(iterator, null, body); 748 assertEquals(expectedType, loop.type()); 749 loop.invoke(Arrays.asList("hello", "world")); 750 } 751 752 @Test(expectedExceptions = NullPointerException.class) testIterateNullBody()753 public static void testIterateNullBody() { 754 MethodHandles.iteratedLoop(MethodHandles.empty(methodType(Iterator.class, int.class)), 755 MethodHandles.identity(int.class), null); 756 } 757 758 @DataProvider wrongIteratorTypes()759 static Object[][] wrongIteratorTypes() { 760 return new Object[][]{{void.class}, {Object.class}, {Iterable.class}}; 761 } 762 763 @Test(dataProvider = "wrongIteratorTypes") testIterateVoidIterator(Class<?> it)764 public static void testIterateVoidIterator(Class<?> it) { 765 boolean caught = false; 766 MethodType v = methodType(it); 767 try { 768 MethodHandles.iteratedLoop(MethodHandles.empty(v), null, MethodHandles.empty(v)); 769 } catch(IllegalArgumentException iae) { 770 assertEqualsFIXME("iteratedLoop first argument must have Iterator return type", iae.getMessage()); 771 caught = true; 772 } 773 assertTrue(caught); 774 } 775 776 @Test(dataProvider = "iteratorInits") testIterateVoidInit(MethodHandle iterator)777 public static void testIterateVoidInit(MethodHandle iterator) throws Throwable { 778 // this test uses List as its loop state type; don't try to change that 779 if (iterator != null) 780 iterator = iterator.asType(iterator.type().changeParameterType(0, List.class)); 781 MethodHandle loop = MethodHandles.iteratedLoop(iterator, Iterate.MH_voidInit, Iterate.MH_printStep); 782 assertEquals(Iterate.MT_print, loop.type()); 783 loop.invoke(Arrays.asList("hello", "world")); 784 } 785 786 @DataProvider iterateParameters()787 static Object[][] iterateParameters() { 788 MethodType i = methodType(int.class); 789 MethodType sil_v = methodType(void.class, String.class, int.class, List.class); 790 MethodType isl_i = methodType(int.class, int.class, String.class, List.class); 791 MethodType isli_i = methodType(int.class, int.class, String.class, List.class, int.class); 792 MethodType sl_v = methodType(void.class, String.class, List.class); 793 MethodType sli_v = methodType(void.class, String.class, List.class, int.class); 794 MethodType l_it = methodType(Iterator.class, List.class); 795 MethodType li_i = methodType(int.class, List.class, int.class); 796 MethodType li_it = methodType(Iterator.class, List.class, int.class); 797 MethodType il_it = methodType(Iterator.class, int.class, List.class); 798 MethodType l_i = methodType(int.class, List.class); 799 return new Object[][]{ 800 {l_it, null, sl_v, ""}, 801 {l_it, l_i, isl_i, ""}, 802 {l_it, null, sl_v, ""}, 803 {li_it, li_i, isli_i, ""}, 804 {null, null, sil_v, "inferred first loop argument must inherit from Iterable: int"}, 805 {il_it, null, sil_v, ""}, 806 {li_it, null, sli_v, ""}, 807 {sl_v, null, sl_v, "iteratedLoop first argument must have Iterator return type"}, 808 {li_it, l_it, sl_v, 809 String.format("iterator and init parameter lists must match: %s != %s", li_it, l_it)}, 810 {li_it, li_i, isl_i, 811 String.format("body types (regard parameter types after index 0, and result type) must match: %s != %s", 812 isl_i, isl_i.dropParameterTypes(0, 1).appendParameterTypes(int.class))} 813 }; 814 } 815 816 @Test(dataProvider = "iterateParameters") testIterateParameters(MethodType it, MethodType in, MethodType bo, String msg)817 public static void testIterateParameters(MethodType it, MethodType in, MethodType bo, String msg) { 818 boolean negative = !msg.isEmpty(); 819 MethodHandle iterator = it == null ? null : MethodHandles.empty(it); 820 MethodHandle init = in == null ? null : MethodHandles.empty(in); 821 boolean caught = false; 822 MethodHandle loop = null; 823 try { 824 loop = MethodHandles.iteratedLoop(iterator, init, MethodHandles.empty(bo)); 825 } catch (Throwable t) { 826 if (!negative) { 827 throw t; 828 } 829 assertEqualsFIXME(msg, t.getMessage()); 830 caught = true; 831 } 832 if (negative) { 833 assertTrue(caught); 834 } else { 835 MethodType lt = loop.type(); 836 if (it == null && in == null) { 837 assertEquals(bo.dropParameterTypes(0, 1), lt); 838 } else if (it == null) { 839 if (in.parameterCount() == 0) { 840 assertEquals(bo.dropParameterTypes(0, in.returnType() == void.class ? 1 : 2), lt); 841 } else { 842 assertEquals(methodType(bo.returnType(), in.parameterArray()), lt); 843 } 844 } else if (in == null) { 845 assertEquals(methodType(bo.returnType(), it.parameterArray()), lt); 846 } else if (it.parameterCount() > in.parameterCount()) { 847 assertEquals(methodType(bo.returnType(), it.parameterArray()), lt); 848 } else if (it.parameterCount() < in.parameterCount()) { 849 assertEquals(methodType(bo.returnType(), in.parameterArray()), lt); 850 } else { 851 // both it, in present; with equal parameter list lengths 852 assertEquals(it.parameterList(), lt.parameterList()); 853 assertEquals(in.parameterList(), lt.parameterList()); 854 assertEquals(bo.returnType(), lt.returnType()); 855 } 856 } 857 } 858 859 @Test testIteratorSubclass()860 public static void testIteratorSubclass() throws Throwable { 861 MethodHandle loop = MethodHandles.iteratedLoop(MethodHandles.empty(methodType(BogusIterator.class, List.class)), 862 null, MethodHandles.empty(methodType(void.class, String.class, List.class))); 863 assertEquals(methodType(void.class, List.class), loop.type()); 864 } 865 866 static class BogusIterator implements Iterator { 867 @Override hasNext()868 public boolean hasNext() { 869 return false; 870 } 871 @Override next()872 public Object next() { 873 return null; 874 } 875 } 876 877 static class Empty { 878 f()879 static void f() { } 880 pred()881 static boolean pred() { 882 return false; 883 } 884 c()885 static int c() { 886 return 23; 887 } 888 889 static final Class<Empty> EMPTY = Empty.class; 890 891 static final MethodType MT_f = methodType(void.class); 892 static final MethodType MT_pred = methodType(boolean.class); 893 static final MethodType MT_c = methodType(int.class); 894 895 static final MethodHandle MH_f; 896 static final MethodHandle MH_pred; 897 static final MethodHandle MH_c; 898 899 static { 900 try { 901 MH_f = LOOKUP.findStatic(EMPTY, "f", MT_f); 902 MH_pred = LOOKUP.findStatic(EMPTY, "pred", MT_pred); 903 MH_c = LOOKUP.findStatic(EMPTY, "c", MT_c); 904 } catch (Exception e) { 905 throw new ExceptionInInitializerError(e); 906 } 907 } 908 } 909 910 static class Fac { 911 zero(int k)912 static int zero(int k) { 913 return 0; 914 } 915 one(int k)916 static int one(int k) { 917 return 1; 918 } 919 pred(int i, int acc, int k)920 static boolean pred(int i, int acc, int k) { 921 return i < k; 922 } 923 inc(int i, int acc, int k)924 static int inc(int i, int acc, int k) { 925 return i + 1; 926 } 927 mult(int i, int acc, int k)928 static int mult(int i, int acc, int k) { 929 return i * acc; 930 } 931 dot(int i, int acc, int k)932 static void dot(int i, int acc, int k) { 933 System.out.print('.'); 934 } 935 fin(int i, int acc, int k)936 static int fin(int i, int acc, int k) { 937 return acc; 938 } 939 940 static final Class<Fac> FAC = Fac.class; 941 942 static final MethodType MT_init = methodType(int.class, int.class); 943 static final MethodType MT_fn = methodType(int.class, int.class, int.class, int.class); 944 static final MethodType MT_dot = methodType(void.class, int.class, int.class, int.class); 945 static final MethodType MT_pred = methodType(boolean.class, int.class, int.class, int.class); 946 947 static final MethodHandle MH_zero; 948 static final MethodHandle MH_one; 949 static final MethodHandle MH_pred; 950 static final MethodHandle MH_inc; 951 static final MethodHandle MH_mult; 952 static final MethodHandle MH_dot; 953 static final MethodHandle MH_fin; 954 955 static final MethodType MT_fac = methodType(int.class, int.class); 956 957 static { 958 try { 959 MH_zero = LOOKUP.findStatic(FAC, "zero", MT_init); 960 MH_one = LOOKUP.findStatic(FAC, "one", MT_init); 961 MH_pred = LOOKUP.findStatic(FAC, "pred", MT_pred); 962 MH_inc = LOOKUP.findStatic(FAC, "inc", MT_fn); 963 MH_mult = LOOKUP.findStatic(FAC, "mult", MT_fn); 964 MH_dot = LOOKUP.findStatic(FAC, "dot", MT_dot); 965 MH_fin = LOOKUP.findStatic(FAC, "fin", MT_fn); 966 } catch (Exception e) { 967 throw new ExceptionInInitializerError(e); 968 } 969 } 970 971 } 972 973 static class Loop { 974 inc(int i, int k)975 static int inc(int i, int k) { 976 return i + 1; 977 } 978 pred(int i, int k)979 static boolean pred(int i, int k) { 980 return i < k; 981 } 982 fin(int i, int k)983 static int fin(int i, int k) { 984 return k; 985 } 986 987 static final Class<Loop> LOOP = Loop.class; 988 989 static final MethodType MT_inc = methodType(int.class, int.class, int.class); 990 static final MethodType MT_pred = methodType(boolean.class, int.class, int.class); 991 static final MethodType MT_fin = methodType(int.class, int.class, int.class); 992 993 static final MethodHandle MH_inc; 994 static final MethodHandle MH_pred; 995 static final MethodHandle MH_fin; 996 997 static final MethodType MT_loop = methodType(int.class, int.class); 998 999 static { 1000 try { 1001 MH_inc = LOOKUP.findStatic(LOOP, "inc", MT_inc); 1002 MH_pred = LOOKUP.findStatic(LOOP, "pred", MT_pred); 1003 MH_fin = LOOKUP.findStatic(LOOP, "fin", MT_fin); 1004 } catch (Exception e) { 1005 throw new ExceptionInInitializerError(e); 1006 } 1007 } 1008 1009 } 1010 1011 static class LoopWithVirtuals { 1012 one(int k)1013 static int one(int k) { 1014 return 1; 1015 } 1016 inc(int i, int acc, int k)1017 int inc(int i, int acc, int k) { 1018 return i + 1; 1019 } 1020 mult(int i, int acc, int k)1021 int mult(int i, int acc, int k) { 1022 return i * acc; 1023 } 1024 pred(int i, int acc, int k)1025 boolean pred(int i, int acc, int k) { 1026 return i < k; 1027 } 1028 fin(int i, int acc, int k)1029 int fin(int i, int acc, int k) { 1030 return acc; 1031 } 1032 1033 static final Class<LoopWithVirtuals> LOOP_WITH_VIRTUALS = LoopWithVirtuals.class; 1034 1035 static final MethodType MT_one = methodType(int.class, int.class); 1036 static final MethodType MT_inc = methodType(int.class, int.class, int.class, int.class); 1037 static final MethodType MT_mult = methodType(int.class, int.class, int.class, int.class); 1038 static final MethodType MT_pred = methodType(boolean.class, int.class, int.class, int.class); 1039 static final MethodType MT_fin = methodType(int.class, int.class, int.class, int.class); 1040 1041 static final MethodHandle MH_one; 1042 static final MethodHandle MH_inc; 1043 static final MethodHandle MH_mult; 1044 static final MethodHandle MH_pred; 1045 static final MethodHandle MH_fin; 1046 1047 static final MethodType MT_loop = methodType(int.class, LOOP_WITH_VIRTUALS, int.class); 1048 1049 static { 1050 try { 1051 MH_one = LOOKUP.findStatic(LOOP_WITH_VIRTUALS, "one", MT_one); 1052 MH_inc = LOOKUP.findVirtual(LOOP_WITH_VIRTUALS, "inc", MT_inc); 1053 MH_mult = LOOKUP.findVirtual(LOOP_WITH_VIRTUALS, "mult", MT_mult); 1054 MH_pred = LOOKUP.findVirtual(LOOP_WITH_VIRTUALS, "pred", MT_pred); 1055 MH_fin = LOOKUP.findVirtual(LOOP_WITH_VIRTUALS, "fin", MT_fin); 1056 } catch (Exception e) { 1057 throw new ExceptionInInitializerError(e); 1058 } 1059 } 1060 permute(MethodHandle h)1061 static MethodHandle permute(MethodHandle h) { 1062 // The handles representing virtual methods need to be rearranged to match the required order of arguments 1063 // (loop-local state comes first, then loop arguments). As the receiver comes first in the signature but is 1064 // a loop argument, it must be moved to the appropriate position in the signature. 1065 return MethodHandles.permuteArguments(h, 1066 methodType(h.type().returnType(), int.class, int.class, LOOP_WITH_VIRTUALS, int.class), 2, 0, 1, 3); 1067 } 1068 1069 } 1070 1071 static class While { 1072 zero(int limit)1073 static int zero(int limit) { 1074 return 0; 1075 } 1076 pred(int i, int limit)1077 static boolean pred(int i, int limit) { 1078 return i < limit; 1079 } 1080 step(int i, int limit)1081 static int step(int i, int limit) { 1082 return i + 1; 1083 } 1084 initString()1085 static String initString() { 1086 return "a"; 1087 } 1088 predString(String s)1089 static boolean predString(String s) { 1090 return s.length() != 1; 1091 } 1092 stepString(String s)1093 static String stepString(String s) { 1094 return s + "a"; 1095 } 1096 zipInitZip(Iterator<String> a, Iterator<String> b)1097 static List<String> zipInitZip(Iterator<String> a, Iterator<String> b) { 1098 return new ArrayList<>(); 1099 } 1100 zipPred(List<String> zip, Iterator<String> a, Iterator<String> b)1101 static boolean zipPred(List<String> zip, Iterator<String> a, Iterator<String> b) { 1102 return a.hasNext() && b.hasNext(); 1103 } 1104 zipStep(List<String> zip, Iterator<String> a, Iterator<String> b)1105 static List<String> zipStep(List<String> zip, Iterator<String> a, Iterator<String> b) { 1106 zip.add(a.next()); 1107 zip.add(b.next()); 1108 return zip; 1109 } 1110 1111 private int i = 0; 1112 voidInit(int k)1113 void voidInit(int k) { 1114 // empty 1115 } 1116 voidBody(int k)1117 void voidBody(int k) { 1118 ++i; 1119 } 1120 voidPred(int k)1121 boolean voidPred(int k) { 1122 return i < k; 1123 } 1124 1125 static final Class<While> WHILE = While.class; 1126 1127 static final MethodType MT_zero = methodType(int.class, int.class); 1128 static final MethodType MT_pred = methodType(boolean.class, int.class, int.class); 1129 static final MethodType MT_fn = methodType(int.class, int.class, int.class); 1130 static final MethodType MT_initString = methodType(String.class); 1131 static final MethodType MT_predString = methodType(boolean.class, String.class); 1132 static final MethodType MT_stepString = methodType(String.class, String.class); 1133 static final MethodType MT_zipInitZip = methodType(List.class, Iterator.class, Iterator.class); 1134 static final MethodType MT_zipPred = methodType(boolean.class, List.class, Iterator.class, Iterator.class); 1135 static final MethodType MT_zipStep = methodType(List.class, List.class, Iterator.class, Iterator.class); 1136 static final MethodType MT_voidInit = methodType(void.class, int.class); 1137 static final MethodType MT_voidBody = methodType(void.class, int.class); 1138 static final MethodType MT_voidPred = methodType(boolean.class, int.class); 1139 1140 static final MethodHandle MH_zero; 1141 static final MethodHandle MH_pred; 1142 static final MethodHandle MH_step; 1143 static final MethodHandle MH_initString; 1144 static final MethodHandle MH_predString; 1145 static final MethodHandle MH_stepString; 1146 static final MethodHandle MH_zipInitZip; 1147 static final MethodHandle MH_zipPred; 1148 static final MethodHandle MH_zipStep; 1149 static final MethodHandle MH_voidInit; 1150 static final MethodHandle MH_voidBody; 1151 static final MethodHandle MH_voidPred; 1152 1153 static final MethodType MT_while = methodType(int.class, int.class); 1154 static final MethodType MT_string = methodType(String.class); 1155 static final MethodType MT_zip = methodType(List.class, Iterator.class, Iterator.class); 1156 static final MethodType MT_void = methodType(void.class, int.class); 1157 1158 static { 1159 try { 1160 MH_zero = LOOKUP.findStatic(WHILE, "zero", MT_zero); 1161 MH_pred = LOOKUP.findStatic(WHILE, "pred", MT_pred); 1162 MH_step = LOOKUP.findStatic(WHILE, "step", MT_fn); 1163 MH_initString = LOOKUP.findStatic(WHILE, "initString", MT_initString); 1164 MH_predString = LOOKUP.findStatic(WHILE, "predString", MT_predString); 1165 MH_stepString = LOOKUP.findStatic(WHILE, "stepString", MT_stepString); 1166 MH_zipInitZip = LOOKUP.findStatic(WHILE, "zipInitZip", MT_zipInitZip); 1167 MH_zipPred = LOOKUP.findStatic(WHILE, "zipPred", MT_zipPred); 1168 MH_zipStep = LOOKUP.findStatic(WHILE, "zipStep", MT_zipStep); 1169 MH_voidInit = LOOKUP.findVirtual(WHILE, "voidInit", MT_voidInit); 1170 MH_voidBody = LOOKUP.findVirtual(WHILE, "voidBody", MT_voidBody); 1171 MH_voidPred = LOOKUP.findVirtual(WHILE, "voidPred", MT_voidPred); 1172 } catch (Exception e) { 1173 throw new ExceptionInInitializerError(e); 1174 } 1175 } 1176 1177 } 1178 1179 static class Counted { 1180 start(String arg)1181 static String start(String arg) { 1182 return arg; 1183 } 1184 step(String v, int counter)1185 static String step(String v, int counter) { 1186 return "na " + v; 1187 } 1188 stepUpdateArray(int counter, int[] a)1189 static void stepUpdateArray(int counter, int[] a) { 1190 ++a[0]; 1191 } 1192 printHello(int counter)1193 static void printHello(int counter) { 1194 System.out.print("hello"); 1195 } 1196 addCounter(int x, int counter)1197 static int addCounter(int x, int counter) { 1198 return x + counter; 1199 } 1200 stateBody(String s, int counter)1201 static String stateBody(String s, int counter) { 1202 return "s" + s + counter; 1203 } 1204 append(String localState, int counter, String loopArg)1205 static String append(String localState, int counter, String loopArg) { 1206 if (null == localState) { 1207 return loopArg + counter; 1208 } 1209 return localState + counter; 1210 } 1211 1212 static final Class<Counted> COUNTED = Counted.class; 1213 1214 static final MethodType MT_start = methodType(String.class, String.class); 1215 static final MethodType MT_step = methodType(String.class, String.class, int.class); 1216 static final MethodType MT_stepUpdateArray = methodType(void.class, int.class, int[].class); 1217 static final MethodType MT_printHello = methodType(void.class, int.class); 1218 static final MethodType MT_addCounter = methodType(int.class, int.class, int.class); 1219 static final MethodType MT_stateBody = methodType(String.class, String.class, int.class); 1220 static final MethodType MT_append = methodType(String.class, String.class, int.class, String.class); 1221 1222 static final MethodHandle MH_13; 1223 static final MethodHandle MH_m5; 1224 static final MethodHandle MH_8; 1225 static final MethodHandle MH_start; 1226 static final MethodHandle MH_step; 1227 static final MethodHandle MH_stepUpdateArray; 1228 static final MethodHandle MH_printHello; 1229 static final MethodHandle MH_addCounter; 1230 static final MethodHandle MH_stateBody; 1231 static final MethodHandle MH_append; 1232 1233 static final MethodType MT_counted = methodType(String.class, String.class); 1234 static final MethodType MT_arrayCounted = methodType(void.class, int[].class); 1235 static final MethodType MT_countedPrinting = methodType(void.class); 1236 static final MethodType MT_counterInit = methodType(int.class); 1237 static final MethodType MT_bodyDeterminesState = methodType(String.class); 1238 static final MethodType MT_iterationsDefineArgs = methodType(String.class, String.class); 1239 1240 static { 1241 try { 1242 MH_13 = MethodHandles.constant(int.class, 13); 1243 MH_m5 = MethodHandles.constant(int.class, -5); 1244 MH_8 = MethodHandles.constant(int.class, 8); 1245 MH_start = LOOKUP.findStatic(COUNTED, "start", MT_start); 1246 MH_step = LOOKUP.findStatic(COUNTED, "step", MT_step); 1247 MH_stepUpdateArray = LOOKUP.findStatic(COUNTED, "stepUpdateArray", MT_stepUpdateArray); 1248 MH_printHello = LOOKUP.findStatic(COUNTED, "printHello", MT_printHello); 1249 MH_addCounter = LOOKUP.findStatic(COUNTED, "addCounter", MT_addCounter); 1250 MH_stateBody = LOOKUP.findStatic(COUNTED, "stateBody", MT_stateBody); 1251 MH_append = LOOKUP.findStatic(COUNTED, "append", MT_append); 1252 } catch (Exception e) { 1253 throw new ExceptionInInitializerError(e); 1254 } 1255 } 1256 1257 } 1258 1259 static class Iterate { 1260 sumIterator(Integer[] a)1261 static Iterator<Integer> sumIterator(Integer[] a) { 1262 return Arrays.asList(a).iterator(); 1263 } 1264 sumInit(Integer[] a)1265 static int sumInit(Integer[] a) { 1266 return 0; 1267 } 1268 sumStep(int s, int e, Integer[] a)1269 static int sumStep(int s, int e, Integer[] a) { 1270 return s + e; 1271 } 1272 reverseInit(List<String> l)1273 static List<String> reverseInit(List<String> l) { 1274 return new ArrayList<>(); 1275 } 1276 reverseStep(List<String> r, String e, List<String> l)1277 static List<String> reverseStep(List<String> r, String e, List<String> l) { 1278 r.add(0, e); 1279 return r; 1280 } 1281 lengthInit(List<Double> l)1282 static int lengthInit(List<Double> l) { 1283 return 0; 1284 } 1285 lengthStep(int len, Object o, List<Double> l)1286 static int lengthStep(int len, Object o, List<Double> l) { 1287 return len + 1; 1288 } 1289 mapInit(List<String> l)1290 static List<String> mapInit(List<String> l) { 1291 return new ArrayList<>(); 1292 } 1293 mapStep(List<String> r, String e, List<String> l)1294 static List<String> mapStep(List<String> r, String e, List<String> l) { 1295 r.add(e.toUpperCase()); 1296 return r; 1297 } 1298 printStep(String s, List<String> l)1299 static void printStep(String s, List<String> l) { 1300 System.out.print(s); 1301 } 1302 voidInit(List<String> l)1303 static void voidInit(List<String> l) { 1304 // empty 1305 } 1306 iteratorFromList(List<?> l)1307 static ListIterator<?> iteratorFromList(List<?> l) { 1308 return l.listIterator(); 1309 } 1310 iteratorFromIterable(Iterable<?> l)1311 static Iterator<?> iteratorFromIterable(Iterable<?> l) { 1312 return l.iterator(); 1313 } 1314 1315 static final Class<Iterate> ITERATE = Iterate.class; 1316 1317 static final MethodType MT_sumIterator = methodType(Iterator.class, Integer[].class); 1318 1319 static final MethodType MT_sumInit = methodType(int.class, Integer[].class); 1320 static final MethodType MT_reverseInit = methodType(List.class, List.class); 1321 static final MethodType MT_lenghInit = methodType(int.class, List.class); 1322 static final MethodType MT_mapInit = methodType(List.class, List.class); 1323 1324 static final MethodType MT_sumStep = methodType(int.class, int.class, int.class, Integer[].class); 1325 static final MethodType MT_reverseStep = methodType(List.class, List.class, String.class, List.class); 1326 static final MethodType MT_lengthStep = methodType(int.class, int.class, Object.class, List.class); 1327 static final MethodType MT_mapStep = methodType(List.class, List.class, String.class, List.class); 1328 static final MethodType MT_printStep = methodType(void.class, String.class, List.class); 1329 1330 static final MethodType MT_voidInit = methodType(void.class, List.class); 1331 1332 static final MethodType MT_iteratorFromList = methodType(ListIterator.class, List.class); 1333 static final MethodType MT_iteratorFromIterable = methodType(Iterator.class, Iterable.class); 1334 1335 static final MethodHandle MH_sumIterator; 1336 static final MethodHandle MH_sumInit; 1337 static final MethodHandle MH_sumStep; 1338 static final MethodHandle MH_printStep; 1339 1340 static final MethodHandle MH_reverseInit; 1341 static final MethodHandle MH_reverseStep; 1342 1343 static final MethodHandle MH_lengthInit; 1344 static final MethodHandle MH_lengthStep; 1345 1346 static final MethodHandle MH_mapInit; 1347 static final MethodHandle MH_mapStep; 1348 1349 static final MethodHandle MH_voidInit; 1350 1351 static final MethodHandle MH_iteratorFromList; 1352 static final MethodHandle MH_iteratorFromIterable; 1353 1354 static final MethodType MT_sum = methodType(int.class, Integer[].class); 1355 static final MethodType MT_reverse = methodType(List.class, List.class); 1356 static final MethodType MT_length = methodType(int.class, List.class); 1357 static final MethodType MT_map = methodType(List.class, List.class); 1358 static final MethodType MT_print = methodType(void.class, List.class); 1359 1360 static { 1361 try { 1362 MH_sumIterator = LOOKUP.findStatic(ITERATE, "sumIterator", MT_sumIterator); 1363 MH_sumInit = LOOKUP.findStatic(ITERATE, "sumInit", MT_sumInit); 1364 MH_sumStep = LOOKUP.findStatic(ITERATE, "sumStep", MT_sumStep); 1365 MH_reverseInit = LOOKUP.findStatic(ITERATE, "reverseInit", MT_reverseInit); 1366 MH_reverseStep = LOOKUP.findStatic(ITERATE, "reverseStep", MT_reverseStep); 1367 MH_lengthInit = LOOKUP.findStatic(ITERATE, "lengthInit", MT_lenghInit); 1368 MH_lengthStep = LOOKUP.findStatic(ITERATE, "lengthStep", MT_lengthStep); 1369 MH_mapInit = LOOKUP.findStatic(ITERATE, "mapInit", MT_mapInit); 1370 MH_mapStep = LOOKUP.findStatic(ITERATE, "mapStep", MT_mapStep); 1371 MH_printStep = LOOKUP.findStatic(ITERATE, "printStep", MT_printStep); 1372 MH_voidInit = LOOKUP.findStatic(ITERATE, "voidInit", MT_voidInit); 1373 MH_iteratorFromList = LOOKUP.findStatic(ITERATE, "iteratorFromList", MT_iteratorFromList); 1374 MH_iteratorFromIterable = LOOKUP.findStatic(ITERATE, "iteratorFromIterable", MT_iteratorFromIterable); 1375 } catch (Exception e) { 1376 throw new ExceptionInInitializerError(e); 1377 } 1378 } 1379 1380 } 1381 1382 } 1383