1 /*
   2  * Copyright (c) 2011, 2012, 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  * @summary unit tests for recursive method handles
  28  * @run junit/othervm/timeout=3600 -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies -DRicochetTest.MAX_ARITY=10 test.java.lang.invoke.RicochetTest
  29  */
  30 /*
  31  * @ignore The following test creates an unreasonable number of adapters in -Xcomp mode (7049122)
  32  * @run junit/othervm -DRicochetTest.MAX_ARITY=255 test.java.lang.invoke.RicochetTest
  33  */
  34 
  35 package test.java.lang.invoke;
  36 
  37 import java.lang.invoke.*;
  38 import java.util.*;
  39 import org.junit.*;
  40 import static java.lang.invoke.MethodType.*;
  41 import static java.lang.invoke.MethodHandles.*;
  42 import static org.junit.Assert.*;
  43 
  44 
  45 /**
  46  *
  47  * @author jrose
  48  */
  49 public class RicochetTest {
  50     private static final Class<?> CLASS = RicochetTest.class;
  51     private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40);
  52 
  53     public static void main(String... av) throws Throwable {
  54         RicochetTest test = new RicochetTest();
  55         if (av.length > 0)  test.testOnly = Arrays.asList(av).toString();
  56         if (REPEAT == 1 || test.testOnly != null) {
  57             test.testAll();
  58             if (test.testOnlyTests == null)  throw new RuntimeException("no matching test: "+test.testOnly);
  59         } else if (REPEAT == 0) {
  60             org.junit.runner.JUnitCore.runClasses(RicochetTest.class);
  61         } else {
  62             verbose(1, "REPEAT="+REPEAT);
  63             for (int i = 0; i < REPEAT; i++) {
  64                 test.testRepetition = (i+1);
  65                 verbose(0, "[#"+test.testRepetition+"]");
  66                 test.testAll();
  67             }
  68         }
  69     }
  70     int testRepetition;
  71 
  72     public void testAll() throws Throwable {
  73         testNull();
  74         testBoxInteger();
  75         testFilterReturnValue();
  76         testFilterObject();
  77         testBoxLong();
  78         testFilterInteger();
  79         testIntSpreads();
  80         testByteSpreads();
  81         testLongSpreads();
  82         testIntCollects();
  83         testReturns();
  84         testRecursion();
  85     }
  86 
  87     @Test
  88     public void testNull() throws Throwable {
  89         if (testRepetition > (1+REPEAT/100))  return;  // trivial test
  90         if (!startTest("testNull"))  return;
  91         assertEquals(opI(37), opI.invokeWithArguments(37));
  92         assertEqualFunction(opI, opI);
  93     }
  94 
  95     @Test
  96     public void testBoxInteger() throws Throwable {
  97         if (!startTest("testBoxInteger"))  return;
  98         assertEqualFunction(opI, opI.asType(opL_I.type()).asType(opI.type()));
  99     }
 100 
 101     @Test
 102     public void testFilterReturnValue() throws Throwable {
 103         if (!startTest("testFilterReturnValue"))  return;
 104         int[] ints = { 12, 23, 34, 45, 56, 67, 78, 89 };
 105         Object res = list8ints.invokeExact(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]);
 106         assertEquals(Arrays.toString(ints), res.toString());
 107         MethodHandle idreturn = filterReturnValue(list8ints, identity(Object.class));
 108         res = idreturn.invokeExact(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]);
 109         assertEquals(Arrays.toString(ints), res.toString());
 110         MethodHandle add0 = addL.bindTo(0);
 111         assertEqualFunction(filterReturnValue(opL2, add0), opL2);
 112     }
 113 
 114     @Test
 115     public void testFilterObject() throws Throwable {
 116         if (!startTest("testFilterObject"))  return;
 117         MethodHandle add0 = addL.bindTo(0);
 118         assertEqualFunction(sequence(opL2, add0), opL2);
 119         int bump13 = -13;  // value near 20 works as long as test values are near [-80..80]
 120         MethodHandle add13   = addL.bindTo(bump13);
 121         MethodHandle add13_0 = addL.bindTo(opI2(bump13, 0));
 122         MethodHandle add13_1 = addL.bindTo(opI2(0, bump13));
 123         assertEqualFunction(sequence(opL2, add13_0),
 124                             filterArguments(opL2, 0, add13));
 125         assertEqualFunction(sequence(opL2, add13_1),
 126                             filterArguments(opL2, 1, add13));
 127         System.out.println("[testFilterObject done]");
 128     }
 129 
 130     @Test
 131     public void testBoxLong() throws Throwable {
 132         if (!startTest("testBoxLong"))  return;
 133         assertEqualFunction(opJ, opJ.asType(opL_J.type()).asType(opJ.type()));
 134     }
 135 
 136     @Test
 137     public void testFilterInteger() throws Throwable {
 138         if (!startTest("testFilterInteger"))  return;
 139         assertEqualFunction(opI, sequence(convI_L, opL_I));
 140     }
 141 
 142     @Test
 143     public void testIntSpreads() throws Throwable {
 144         if (!startTest("testIntSpreads"))  return;
 145         MethodHandle id = identity(int[].class);
 146         final int MAX = MAX_ARITY-2;  // 253+1 would cause parameter overflow with 'this' added
 147         for (int nargs = 0; nargs <= MAX; nargs++) {
 148             if (nargs > 30 && nargs < MAX-20)  nargs += 10;
 149             int[] args = new int[nargs];
 150             for (int j = 0; j < args.length; j++)  args[j] = j + 11;
 151             //System.out.println("testIntSpreads "+Arrays.toString(args));
 152             int[] args1 = (int[]) id.invokeExact(args);
 153             assertArrayEquals(args, args1);
 154             MethodHandle coll = id.asCollector(int[].class, nargs);
 155             int[] args2 = args;
 156             switch (nargs) {
 157                 case 0:  args2 = (int[]) coll.invokeExact(); break;
 158                 case 1:  args2 = (int[]) coll.invokeExact(args[0]); break;
 159                 case 2:  args2 = (int[]) coll.invokeExact(args[0], args[1]); break;
 160                 case 3:  args2 = (int[]) coll.invokeExact(args[0], args[1], args[2]); break;
 161                 case 4:  args2 = (int[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
 162                 case 5:  args2 = (int[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
 163             }
 164             assertArrayEquals(args, args2);
 165             MethodHandle mh = coll.asSpreader(int[].class, nargs);
 166             int[] args3 = (int[]) mh.invokeExact(args);
 167             assertArrayEquals(args, args3);
 168         }
 169     }
 170 
 171     @Test
 172     public void testByteSpreads() throws Throwable {
 173         if (!startTest("testByteSpreads"))  return;
 174         MethodHandle id = identity(byte[].class);
 175         final int MAX = MAX_ARITY-2;  // 253+1 would cause parameter overflow with 'this' added
 176         for (int nargs = 0; nargs <= MAX; nargs++) {
 177             if (nargs > 30 && nargs < MAX-20)  nargs += 10;
 178             byte[] args = new byte[nargs];
 179             for (int j = 0; j < args.length; j++)  args[j] = (byte)(j + 11);
 180             //System.out.println("testByteSpreads "+Arrays.toString(args));
 181             byte[] args1 = (byte[]) id.invokeExact(args);
 182             assertArrayEquals(args, args1);
 183             MethodHandle coll = id.asCollector(byte[].class, nargs);
 184             byte[] args2 = args;
 185             switch (nargs) {
 186                 case 0:  args2 = (byte[]) coll.invokeExact(); break;
 187                 case 1:  args2 = (byte[]) coll.invokeExact(args[0]); break;
 188                 case 2:  args2 = (byte[]) coll.invokeExact(args[0], args[1]); break;
 189                 case 3:  args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2]); break;
 190                 case 4:  args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
 191                 case 5:  args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
 192             }
 193             assertArrayEquals(args, args2);
 194             MethodHandle mh = coll.asSpreader(byte[].class, nargs);
 195             byte[] args3 = (byte[]) mh.invokeExact(args);
 196             assertArrayEquals(args, args3);
 197         }
 198     }
 199 
 200     @Test
 201     public void testLongSpreads() throws Throwable {
 202         if (!startTest("testLongSpreads"))  return;
 203         MethodHandle id = identity(long[].class);
 204         final int MAX = (MAX_ARITY - 2) / 2;  // 253/2+1 would cause parameter overflow with 'this' added
 205         for (int nargs = 0; nargs <= MAX; nargs++) {
 206             if (nargs > 30 && nargs < MAX-20)  nargs += 10;
 207             long[] args = new long[nargs];
 208             for (int j = 0; j < args.length; j++)  args[j] = (long)(j + 11);
 209             //System.out.println("testLongSpreads "+Arrays.toString(args));
 210             long[] args1 = (long[]) id.invokeExact(args);
 211             assertArrayEquals(args, args1);
 212             MethodHandle coll = id.asCollector(long[].class, nargs);
 213             long[] args2 = args;
 214             switch (nargs) {
 215                 case 0:  args2 = (long[]) coll.invokeExact(); break;
 216                 case 1:  args2 = (long[]) coll.invokeExact(args[0]); break;
 217                 case 2:  args2 = (long[]) coll.invokeExact(args[0], args[1]); break;
 218                 case 3:  args2 = (long[]) coll.invokeExact(args[0], args[1], args[2]); break;
 219                 case 4:  args2 = (long[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
 220                 case 5:  args2 = (long[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
 221             }
 222             assertArrayEquals(args, args2);
 223             MethodHandle mh = coll.asSpreader(long[].class, nargs);
 224             long[] args3 = (long[]) mh.invokeExact(args);
 225             assertArrayEquals(args, args3);
 226         }
 227     }
 228 
 229     @Test
 230     public void testIntCollects() throws Throwable {
 231         if (!startTest("testIntCollects"))  return;
 232         for (MethodHandle lister : INT_LISTERS) {
 233             int outputs = lister.type().parameterCount();
 234             for (int collects = 0; collects <= Math.min(outputs, INT_COLLECTORS.length-1); collects++) {
 235                 int inputs = outputs - 1 + collects;
 236                 if (inputs < 0)  continue;
 237                 for (int pos = 0; pos + collects <= inputs; pos++) {
 238                     MethodHandle collector = INT_COLLECTORS[collects];
 239                     int[] args = new int[inputs];
 240                     int ap = 0, arg = 31;
 241                     for (int i = 0; i < pos; i++)
 242                         args[ap++] = arg++ + 0;
 243                     for (int i = 0; i < collects; i++)
 244                         args[ap++] = arg++ + 10;
 245                     while (ap < args.length)
 246                         args[ap++] = arg++ + 20;
 247                     // calculate piecemeal:
 248                     //System.out.println("testIntCollects "+Arrays.asList(lister, pos, collector)+" on "+Arrays.toString(args));
 249                     int[] collargs = Arrays.copyOfRange(args, pos, pos+collects);
 250                     int coll = (int) collector.asSpreader(int[].class, collargs.length).invokeExact(collargs);
 251                     int[] listargs = Arrays.copyOfRange(args, 0, outputs);
 252                     System.arraycopy(args, pos+collects, listargs, pos+1, outputs - (pos+1));
 253                     listargs[pos] = coll;
 254                     //System.out.println("  coll="+coll+" listargs="+Arrays.toString(listargs));
 255                     Object expect = lister.asSpreader(int[].class, listargs.length).invokeExact(listargs);
 256                     //System.out.println("  expect="+expect);
 257 
 258                     // now use the combined MH, and test the output:
 259                     MethodHandle mh = collectArguments(lister, pos, int[].class, INT_COLLECTORS[collects]);
 260                     if (mh == null)  continue;  // no infix collection, yet
 261                     assert(mh.type().parameterCount() == inputs);
 262                     Object observe = mh.asSpreader(int[].class, args.length).invokeExact(args);
 263                     assertEquals(expect, observe);
 264                 }
 265             }
 266         }
 267     }
 268 
 269     @Test
 270     public void testByteCollects() throws Throwable {
 271         if (!startTest("testByteCollects"))  return;
 272         for (MethodHandle lister : BYTE_LISTERS) {
 273             int outputs = lister.type().parameterCount();
 274             for (int collects = 0; collects <= Math.min(outputs, BYTE_COLLECTORS.length-1); collects++) {
 275                 int inputs = outputs - 1 + collects;
 276                 if (inputs < 0)  continue;
 277                 for (int pos = 0; pos + collects <= inputs; pos++) {
 278                     MethodHandle collector = BYTE_COLLECTORS[collects];
 279                     byte[] args = new byte[inputs];
 280                     int ap = 0, arg = 31;
 281                     for (int i = 0; i < pos; i++)
 282                         args[ap++] = (byte)(arg++ + 0);
 283                     for (int i = 0; i < collects; i++)
 284                         args[ap++] = (byte)(arg++ + 10);
 285                     while (ap < args.length)
 286                         args[ap++] = (byte)(arg++ + 20);
 287                     // calculate piecemeal:
 288                     //System.out.println("testIntCollects "+Arrays.asList(lister, pos, collector)+" on "+Arrays.toString(args));
 289                     byte[] collargs = Arrays.copyOfRange(args, pos, pos+collects);
 290                     byte coll = (byte) collector.asSpreader(byte[].class, collargs.length).invokeExact(collargs);
 291                     byte[] listargs = Arrays.copyOfRange(args, 0, outputs);
 292                     System.arraycopy(args, pos+collects, listargs, pos+1, outputs - (pos+1));
 293                     listargs[pos] = coll;
 294                     //System.out.println("  coll="+coll+" listargs="+Arrays.toString(listargs));
 295                     Object expect = lister.asSpreader(byte[].class, listargs.length).invokeExact(listargs);
 296                     //System.out.println("  expect="+expect);
 297 
 298                     // now use the combined MH, and test the output:
 299                     MethodHandle mh = collectArguments(lister, pos, byte[].class, BYTE_COLLECTORS[collects]);
 300                     if (mh == null)  continue;  // no infix collection, yet
 301                     assert(mh.type().parameterCount() == inputs);
 302                     Object observe = mh.asSpreader(byte[].class, args.length).invokeExact(args);
 303                     assertEquals(expect, observe);
 304                 }
 305             }
 306         }
 307     }
 308 
 309     private static MethodHandle collectArguments(MethodHandle lister, int pos, Class<?> array, MethodHandle collector) {
 310         int collects = collector.type().parameterCount();
 311         int outputs = lister.type().parameterCount();
 312         if (pos == outputs - 1)
 313             return MethodHandles.filterArguments(lister, pos,
 314                         collector.asSpreader(array, collects))
 315                             .asCollector(array, collects);
 316         //return MethodHandles.collectArguments(lister, pos, collector); //no such animal
 317         return null;
 318     }
 319 
 320     private static final Class<?>[] RETURN_TYPES = {
 321         Object.class, String.class, Integer.class,
 322         int.class, long.class,
 323         boolean.class, byte.class, char.class, short.class,
 324         float.class, double.class,
 325         void.class,
 326     };
 327 
 328     @Test
 329     public void testReturns() throws Throwable {
 330         if (!startTest("testReturns"))  return;
 331         // fault injection:
 332         int faultCount = 0;  // total of 1296 tests
 333         faultCount = Integer.getInteger("testReturns.faultCount", 0);
 334         for (Class<?> ret : RETURN_TYPES) {
 335             // make a complicated identity function and pass something through it
 336             System.out.println(ret.getSimpleName());
 337             Class<?> vret = (ret == void.class) ? Void.class : ret;
 338             MethodHandle id = // (vret)->ret
 339                 identity(vret).asType(methodType(ret, vret));
 340             final int LENGTH = 4;
 341             int[] index = {0};
 342             Object vals = java.lang.reflect.Array.newInstance(vret, LENGTH);
 343             MethodHandle indexGetter =  //()->int
 344                 insertArguments(arrayElementGetter(index.getClass()), 0, index, 0);
 345             MethodHandle valSelector =  // (int)->vret
 346                 arrayElementGetter(vals.getClass()).bindTo(vals);
 347             MethodHandle valGetter =  // ()->vret
 348                 foldArguments(valSelector, indexGetter);
 349             if (ret != void.class) {
 350                 for (int i = 0; i < LENGTH; i++) {
 351                     Object val = (i + 50);
 352                     if (ret == boolean.class)  val = (i % 3 == 0);
 353                     if (ret == String.class)   val = "#"+i;
 354                     if (ret == char.class)     val = (char)('a'+i);
 355                     if (ret == byte.class)     val = (byte)~i;
 356                     if (ret == short.class)    val = (short)(1<<i);
 357                     java.lang.reflect.Array.set(vals, i, val);
 358                 }
 359             }
 360             for (int i = 0; i < LENGTH; i++) {
 361                 Object val = java.lang.reflect.Array.get(vals, i);
 362                 System.out.println(i+" => "+val);
 363                 index[0] = i;
 364                 if (--faultCount == 0)  index[0] ^= 1;
 365                 Object x = valGetter.invokeWithArguments();
 366                 assertEquals(val, x);
 367                 // make a return-filter call:  x = id(valGetter())
 368                 if (--faultCount == 0)  index[0] ^= 1;
 369                 x = filterReturnValue(valGetter, id).invokeWithArguments();
 370                 assertEquals(val, x);
 371                 // make a filter call:  x = id(*,valGetter(),*)
 372                 for (int len = 1; len <= 4; len++) {
 373                     for (int pos = 0; pos < len; pos++) {
 374                         MethodHandle proj = id;  // lambda(..., vret x,...){x}
 375                         for (int j = 0; j < len; j++) {
 376                             if (j == pos)  continue;
 377                             proj = dropArguments(proj, j, Object.class);
 378                         }
 379                         assert(proj.type().parameterCount() == len);
 380                         // proj: (Object*, pos: vret, Object*)->ret
 381                         assertEquals(vret, proj.type().parameterType(pos));
 382                         MethodHandle vgFilter = dropArguments(valGetter, 0, Object.class);
 383                         if (--faultCount == 0)  index[0] ^= 1;
 384                         x = filterArguments(proj, pos, vgFilter).invokeWithArguments(new Object[len]);
 385                         assertEquals(val, x);
 386                     }
 387                 }
 388                 // make a fold call:
 389                 for (int len = 0; len <= 4; len++) {
 390                     for (int fold = 0; fold <= len; fold++) {
 391                         MethodHandle proj = id;  // lambda(ret x, ...){x}
 392                         if (ret == void.class)  proj = constant(Object.class, null);
 393                         int arg0 = (ret == void.class ? 0 : 1);
 394                         for (int j = 0; j < len; j++) {
 395                             proj = dropArguments(proj, arg0, Object.class);
 396                         }
 397                         assert(proj.type().parameterCount() == arg0 + len);
 398                         // proj: (Object*, pos: vret, Object*)->ret
 399                         if (arg0 != 0)  assertEquals(vret, proj.type().parameterType(0));
 400                         MethodHandle vgFilter = valGetter.asType(methodType(ret));
 401                         for (int j = 0; j < fold; j++) {
 402                             vgFilter = dropArguments(vgFilter, j, Object.class);
 403                         }
 404                         x = foldArguments(proj, vgFilter).invokeWithArguments(new Object[len]);
 405                         if (--faultCount == 0)  index[0] ^= 1;
 406                         assertEquals(val, x);
 407                     }
 408                 }
 409             }
 410         }
 411         //System.out.println("faultCount="+faultCount);
 412     }
 413 
 414     @Test
 415     public void testRecursion() throws Throwable {
 416         if (!startTest("testRecursion"))  return;
 417         final int LIMIT = 10;
 418         for (int i = 0; i < LIMIT; i++) {
 419             RFCB rfcb = new RFCB(i);
 420             Object x = "x", y = "y";
 421             Object result = rfcb.recursiveFunction(x, y);
 422             verbose(1, result);
 423         }
 424     }
 425     /** Recursive Function Control Block */
 426     private static class RFCB {
 427         java.util.Random random;
 428         final MethodHandle[] fns;
 429         int depth;
 430         @SuppressWarnings("LeakingThisInConstructor")
 431         RFCB(int seed) throws Throwable {
 432             this.random = new java.util.Random(seed);
 433             this.fns = new MethodHandle[Math.max(29, (1 << MAX_DEPTH-2)/3)];
 434             java.util.Arrays.fill(fns, lookup().bind(this, "recursiveFunction", genericMethodType(2)));
 435             for (int i = 5; i < fns.length; i++) {
 436                 switch (i % 4) {
 437                 case 0: fns[i] = filterArguments(fns[i - 5], 0, insertArguments(fns[i - 4], 1, ".")); break;
 438                 case 1: fns[i] = filterArguments(fns[i - 5], 1, insertArguments(fns[i - 3], 1, ".")); break;
 439                 case 2: fns[i] = filterReturnValue(fns[i - 5], insertArguments(fns[i - 2], 1, ".")); break;
 440                 }
 441             }
 442         }
 443         Object recursiveFunction(Object x, Object y) throws Throwable {
 444             depth++;
 445             try {
 446                 final int ACTION_COUNT = 11;
 447                 switch (random.nextInt(ACTION_COUNT)) {
 448                 case 1:
 449                     Throwable ex = new RuntimeException();
 450                     ex.fillInStackTrace();
 451                     if (VERBOSITY >= 2) ex.printStackTrace(System.out);
 452                     x = "ST; " + x;
 453                     break;
 454                 case 2:
 455                     System.gc();
 456                     x = "GC; " + x;
 457                     break;
 458                 }
 459                 boolean isLeaf = (depth >= MAX_DEPTH);
 460                 if (isLeaf) {
 461                     return Arrays.asList(x, y).toString();
 462                 }
 463                 return fns[random.nextInt(fns.length)].invokeExact(x, y);
 464             } finally {
 465                 depth--;
 466             }
 467         }
 468     }
 469 
 470     private static MethodHandle sequence(MethodHandle mh1, MethodHandle... mhs) {
 471         MethodHandle res = mh1;
 472         for (MethodHandle mh2 : mhs)
 473             res = filterReturnValue(res, mh2);
 474         return res;
 475     }
 476     private static void assertEqualFunction(MethodHandle x, MethodHandle y) throws Throwable {
 477         assertEquals(x.type(), y.type()); //??
 478         MethodType t = x.type();
 479         if (t.parameterCount() == 0) {
 480             assertEqualFunctionAt(null, x, y);
 481             return;
 482         }
 483         Class<?> ptype = t.parameterType(0);
 484         if (ptype == long.class || ptype == Long.class) {
 485             for (long i = -10; i <= 10; i++) {
 486                 assertEqualFunctionAt(i, x, y);
 487             }
 488         } else {
 489             for (int i = -10; i <= 10; i++) {
 490                 assertEqualFunctionAt(i, x, y);
 491             }
 492         }
 493     }
 494     private static void assertEqualFunctionAt(Object v, MethodHandle x, MethodHandle y) throws Throwable {
 495         Object[] args = new Object[x.type().parameterCount()];
 496         Arrays.fill(args, v);
 497         Object xval = invokeWithCatch(x, args);
 498         Object yval = invokeWithCatch(y, args);
 499         String msg = "ok";
 500         if (!Objects.equals(xval, yval)) {
 501             msg = ("applying "+x+" & "+y+" to "+v);
 502         }
 503         assertEquals(msg, xval, yval);
 504     }
 505     private static Object invokeWithCatch(MethodHandle mh, Object... args) throws Throwable {
 506         try {
 507             return mh.invokeWithArguments(args);
 508         } catch (Throwable ex) {
 509             System.out.println("threw: "+mh+Arrays.asList(args));
 510             ex.printStackTrace(System.out);
 511             return ex;
 512         }
 513     }
 514 
 515     private static final Lookup LOOKUP = lookup();
 516     private static MethodHandle findStatic(String name,
 517                                            Class<?> rtype,
 518                                            Class<?>... ptypes) {
 519         try {
 520             return LOOKUP.findStatic(LOOKUP.lookupClass(), name, methodType(rtype, ptypes));
 521         } catch (ReflectiveOperationException ex) {
 522             throw new RuntimeException(ex);
 523         }
 524     }
 525     private static MethodHandle findStatic(String name,
 526                                            Class<?> rtype,
 527                                            List<?> ptypes) {
 528         return findStatic(name, rtype, ptypes.toArray(new Class<?>[ptypes.size()]));
 529     }
 530     static int getProperty(String name, int dflt) {
 531         String qual = LOOKUP.lookupClass().getName();
 532         String prop = System.getProperty(qual+"."+name);
 533         if (prop == null)  prop = System.getProperty(name);
 534         if (prop == null)  return dflt;
 535         return Integer.parseInt(prop);
 536     }
 537 
 538     private static int opI(int... xs) {
 539         stress();
 540         int base = 100;
 541         int z = 0;
 542         for (int x : xs) {
 543             z = (z * base) + (x % base);
 544         }
 545         verbose("opI", xs.length, xs, z);
 546         return z;
 547     }
 548     private static int opI2(int x, int y) { return opI(x, y); }  // x*100 + y%100
 549     private static int opI3(int x, int y, int z) { return opI(x, y, z); }
 550     private static int opI4(int w, int x, int y, int z) { return opI(w, x, y, z); }
 551     private static int opI(int x) { return opI2(x, 37); }
 552     private static Object opI_L(int x) { return (Object) opI(x); }
 553     private static long opJ3(long x, long y, long z) { return (long) opI3((int)x, (int)y, (int)z); }
 554     private static long opJ2(long x, long y) { return (long) opI2((int)x, (int)y); }
 555     private static long opJ(long x) { return (long) opI((int)x); }
 556     private static Object opL2(Object x, Object y) { return (Object) opI2((int)x, (int)y); }
 557     private static Object opL(Object x) { return (Object) opI((int)x); }
 558     private static int opL2_I(Object x, Object y) { return opI2((int)x, (int)y); }
 559     private static int opL_I(Object x) { return opI((int)x); }
 560     private static long opL_J(Object x) { return (long) opI((int)x); }
 561     private static final MethodHandle opI, opI2, opI3, opI4, opI_L, opJ, opJ2, opJ3, opL2, opL, opL2_I, opL_I, opL_J;
 562     static {
 563         opI4 = findStatic("opI4", int.class, int.class, int.class, int.class, int.class);
 564         opI3 = findStatic("opI3", int.class, int.class, int.class, int.class);
 565         opI2 = findStatic("opI2", int.class, int.class, int.class);
 566         opI = findStatic("opI", int.class, int.class);
 567         opI_L = findStatic("opI_L", Object.class, int.class);
 568         opJ = findStatic("opJ", long.class, long.class);
 569         opJ2 = findStatic("opJ2", long.class, long.class, long.class);
 570         opJ3 = findStatic("opJ3", long.class, long.class, long.class, long.class);
 571         opL2 = findStatic("opL2", Object.class, Object.class, Object.class);
 572         opL = findStatic("opL", Object.class, Object.class);
 573         opL2_I = findStatic("opL2_I", int.class, Object.class, Object.class);
 574         opL_I = findStatic("opL_I", int.class, Object.class);
 575         opL_J = findStatic("opL_J", long.class, Object.class);
 576     }
 577     private static final MethodHandle[] INT_COLLECTORS = {
 578         constant(int.class, 42), opI, opI2, opI3, opI4
 579     };
 580     private static final MethodHandle[] BYTE_COLLECTORS = {
 581         constant(byte.class, (byte)42), i2b(opI), i2b(opI2), i2b(opI3), i2b(opI4)
 582     };
 583     private static final MethodHandle[] LONG_COLLECTORS = {
 584         constant(long.class, 42), opJ, opJ2, opJ3
 585     };
 586 
 587     private static int addI(int x, int y) { stress(); return x+y; }
 588     private static Object addL(Object x, Object y) { return addI((int)x, (int)y); }
 589     private static final MethodHandle addI, addL;
 590     static {
 591         addI = findStatic("addI", int.class, int.class, int.class);
 592         addL = findStatic("addL", Object.class, Object.class, Object.class);
 593     }
 594 
 595     private static Object list8ints(int a, int b, int c, int d, int e, int f, int g, int h) {
 596         return Arrays.asList(a, b, c, d, e, f, g, h);
 597     }
 598     private static Object list8longs(long a, long b, long c, long d, long e, long f, long g, long h) {
 599         return Arrays.asList(a, b, c, d, e, f, g, h);
 600     }
 601     private static final MethodHandle list8ints = findStatic("list8ints", Object.class,
 602                                                              Collections.nCopies(8, int.class));
 603     private static final MethodHandle list8longs = findStatic("list8longs", Object.class,
 604                                                               Collections.nCopies(8, long.class));
 605     private static final MethodHandle[] INT_LISTERS, LONG_LISTERS, BYTE_LISTERS;
 606     static {
 607         int listerCount = list8ints.type().parameterCount() + 1;
 608         INT_LISTERS  = new MethodHandle[listerCount];
 609         LONG_LISTERS = new MethodHandle[listerCount];
 610         BYTE_LISTERS = new MethodHandle[listerCount];
 611         MethodHandle lister = list8ints;
 612         MethodHandle llister = list8longs;
 613         for (int i = listerCount - 1; ; i--) {
 614             INT_LISTERS[i] = lister;
 615             LONG_LISTERS[i] = llister;
 616             BYTE_LISTERS[i] = i2b(lister);
 617             if (i == 0)  break;
 618             lister  = insertArguments(lister,  i-1, 0);
 619             llister = insertArguments(llister, i-1, 0L);
 620         }
 621     }
 622     private static MethodHandle i2b(MethodHandle mh) {
 623         return MethodHandles.explicitCastArguments(mh, subst(mh.type(), int.class, byte.class));
 624     }
 625     private static MethodType subst(MethodType mt, Class<?> from, Class<?> to) {
 626         for (int i = 0; i < mt.parameterCount(); i++) {
 627             if (mt.parameterType(i) == from)
 628                 mt = mt.changeParameterType(i, to);
 629         }
 630         if (mt.returnType() == from)
 631             mt = mt.changeReturnType(to);
 632         return mt;
 633     }
 634 
 635 
 636     private static Object  convI_L(int     x) { stress(); return (Object)  x; }
 637     private static int     convL_I(Object  x) { stress(); return (int)     x; }
 638     private static Object  convJ_L(long    x) { stress(); return (Object)  x; }
 639     private static long    convL_J(Object  x) { stress(); return (long)    x; }
 640     private static int     convJ_I(long    x) { stress(); return (int)     x; }
 641     private static long    convI_J(int     x) { stress(); return (long)    x; }
 642     private static final MethodHandle convI_L, convL_I, convJ_L, convL_J, convJ_I, convI_J;
 643     static {
 644         convI_L = findStatic("convI_L", Object.class, int.class);
 645         convL_I = findStatic("convL_I", int.class, Object.class);
 646         convJ_L = findStatic("convJ_L", Object.class, long.class);
 647         convL_J = findStatic("convL_J", long.class, Object.class);
 648         convJ_I = findStatic("convJ_I", int.class, long.class);
 649         convI_J = findStatic("convI_J", long.class, int.class);
 650     }
 651 
 652     // stress modes:
 653     private static final int MAX_DEPTH = getProperty("MAX_DEPTH", 5);
 654     private static final int REPEAT = getProperty("REPEAT", 0);
 655     private static final int STRESS = getProperty("STRESS", 0);
 656     private static /*v*/ int STRESS_COUNT;
 657     private static final Object[] SINK = new Object[4];
 658     private static void stress() {
 659         if (STRESS <= 0) return;
 660         int count = STRESS + (STRESS_COUNT++ & 0x1);  // non-constant value
 661         for (int i = 0; i < count; i++) {
 662             SINK[i % SINK.length] = new Object[STRESS + i % (SINK.length + 1)];
 663         }
 664     }
 665 
 666     // verbosity:
 667     private static final int VERBOSITY = getProperty("VERBOSITY", 0) + (REPEAT == 0 ? 0 : -1);
 668     private static void verbose(Object a, Object b, Object c, Object d) {
 669         if (VERBOSITY <= 0)  return;
 670         verbose(1, a, b, c, d);
 671     }
 672     private static void verbose(Object a, Object b, Object c) {
 673         if (VERBOSITY <= 0)  return;
 674         verbose(1, a, b, c);
 675     }
 676     private static void verbose(int level, Object a, Object... bcd) {
 677         if (level > VERBOSITY)  return;
 678         String m = a.toString();
 679         if (bcd != null && bcd.length > 0) {
 680             List<Object> l = new ArrayList<>(bcd.length);
 681             for (Object x : bcd) {
 682                 if (x instanceof Object[])  x = Arrays.asList((Object[])x);
 683                 if (x instanceof int[])     x = Arrays.toString((int[])x);
 684                 if (x instanceof long[])    x = Arrays.toString((long[])x);
 685                 l.add(x);
 686             }
 687             m = m+Arrays.asList(bcd);
 688         }
 689         System.out.println(m);
 690     }
 691     String testOnly;
 692     String testOnlyTests;
 693     private boolean startTest(String name) {
 694         if (testOnly != null && !testOnly.contains(name))
 695             return false;
 696         verbose(0, "["+name+"]");
 697         testOnlyTests = (testOnlyTests == null) ? name : testOnlyTests+" "+name;
 698         return true;
 699     }
 700 
 701 }