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