RicochetTest.java revision 9330:8b1f1c2a400f
1238106Sdes/*
2238106Sdes * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
3238106Sdes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4238106Sdes *
5238106Sdes * This code is free software; you can redistribute it and/or modify it
6238106Sdes * under the terms of the GNU General Public License version 2 only, as
7238106Sdes * published by the Free Software Foundation.
8238106Sdes *
9238106Sdes * This code is distributed in the hope that it will be useful, but WITHOUT
10238106Sdes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11238106Sdes * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12238106Sdes * version 2 for more details (a copy is included in the LICENSE file that
13238106Sdes * accompanied this code).
14238106Sdes *
15238106Sdes * You should have received a copy of the GNU General Public License version
16238106Sdes * 2 along with this work; if not, write to the Free Software Foundation,
17238106Sdes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18238106Sdes *
19238106Sdes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20238106Sdes * or visit www.oracle.com if you need additional information or have any
21238106Sdes * questions.
22238106Sdes */
23238106Sdes
24266114Sdes/* @test
25266114Sdes * @summary unit tests for recursive method handles
26266114Sdes * @run junit/othervm/timeout=3600 -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies -DRicochetTest.MAX_ARITY=10 test.java.lang.invoke.RicochetTest
27266114Sdes */
28266114Sdes/*
29266114Sdes * @ignore The following test creates an unreasonable number of adapters in -Xcomp mode (7049122)
30266114Sdes * @run junit/othervm -DRicochetTest.MAX_ARITY=255 test.java.lang.invoke.RicochetTest
31266114Sdes */
32266114Sdes
33266114Sdespackage test.java.lang.invoke;
34238106Sdes
35238106Sdesimport java.lang.invoke.*;
36238106Sdesimport java.util.*;
37238106Sdesimport org.junit.*;
38238106Sdesimport static java.lang.invoke.MethodType.*;
39238106Sdesimport static java.lang.invoke.MethodHandles.*;
40238106Sdesimport static org.junit.Assert.*;
41238106Sdes
42238106Sdes
43238106Sdes/**
44238106Sdes *
45238106Sdes * @author jrose
46238106Sdes */
47238106Sdespublic class RicochetTest {
48238106Sdes    private static final Class<?> CLASS = RicochetTest.class;
49238106Sdes    private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40);
50238106Sdes
51238106Sdes    public static void main(String... av) throws Throwable {
52238106Sdes        RicochetTest test = new RicochetTest();
53238106Sdes        if (av.length > 0)  test.testOnly = Arrays.asList(av).toString();
54238106Sdes        if (REPEAT == 1 || test.testOnly != null) {
55238106Sdes            test.testAll();
56238106Sdes            if (test.testOnlyTests == null)  throw new RuntimeException("no matching test: "+test.testOnly);
57238106Sdes        } else if (REPEAT == 0) {
58238106Sdes            org.junit.runner.JUnitCore.runClasses(RicochetTest.class);
59238106Sdes        } else {
60238106Sdes            verbose(1, "REPEAT="+REPEAT);
61238106Sdes            for (int i = 0; i < REPEAT; i++) {
62238106Sdes                test.testRepetition = (i+1);
63238106Sdes                verbose(0, "[#"+test.testRepetition+"]");
64238106Sdes                test.testAll();
65238106Sdes            }
66238106Sdes        }
67238106Sdes    }
68238106Sdes    int testRepetition;
69238106Sdes
70238106Sdes    public void testAll() throws Throwable {
71238106Sdes        testNull();
72238106Sdes        testBoxInteger();
73238106Sdes        testFilterReturnValue();
74238106Sdes        testFilterObject();
75238106Sdes        testBoxLong();
76238106Sdes        testFilterInteger();
77238106Sdes        testIntSpreads();
78238106Sdes        testByteSpreads();
79238106Sdes        testLongSpreads();
80238106Sdes        testIntCollects();
81238106Sdes        testReturns();
82238106Sdes        testRecursion();
83238106Sdes    }
84238106Sdes
85238106Sdes    @Test
86238106Sdes    public void testNull() throws Throwable {
87238106Sdes        if (testRepetition > (1+REPEAT/100))  return;  // trivial test
88238106Sdes        if (!startTest("testNull"))  return;
89238106Sdes        assertEquals(opI(37), opI.invokeWithArguments(37));
90238106Sdes        assertEqualFunction(opI, opI);
91238106Sdes    }
92238106Sdes
93238106Sdes    @Test
94238106Sdes    public void testBoxInteger() throws Throwable {
95238106Sdes        if (!startTest("testBoxInteger"))  return;
96238106Sdes        assertEqualFunction(opI, opI.asType(opL_I.type()).asType(opI.type()));
97238106Sdes    }
98238106Sdes
99238106Sdes    @Test
100238106Sdes    public void testFilterReturnValue() throws Throwable {
101238106Sdes        if (!startTest("testFilterReturnValue"))  return;
102238106Sdes        int[] ints = { 12, 23, 34, 45, 56, 67, 78, 89 };
103238106Sdes        Object res = list8ints.invokeExact(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]);
104238106Sdes        assertEquals(Arrays.toString(ints), res.toString());
105238106Sdes        MethodHandle idreturn = filterReturnValue(list8ints, identity(Object.class));
106238106Sdes        res = idreturn.invokeExact(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]);
107238106Sdes        assertEquals(Arrays.toString(ints), res.toString());
108238106Sdes        MethodHandle add0 = addL.bindTo(0);
109238106Sdes        assertEqualFunction(filterReturnValue(opL2, add0), opL2);
110238106Sdes    }
111238106Sdes
112238106Sdes    @Test
113238106Sdes    public void testFilterObject() throws Throwable {
114238106Sdes        if (!startTest("testFilterObject"))  return;
115238106Sdes        MethodHandle add0 = addL.bindTo(0);
116238106Sdes        assertEqualFunction(sequence(opL2, add0), opL2);
117238106Sdes        int bump13 = -13;  // value near 20 works as long as test values are near [-80..80]
118238106Sdes        MethodHandle add13   = addL.bindTo(bump13);
119238106Sdes        MethodHandle add13_0 = addL.bindTo(opI2(bump13, 0));
120238106Sdes        MethodHandle add13_1 = addL.bindTo(opI2(0, bump13));
121296415Sdes        assertEqualFunction(sequence(opL2, add13_0),
122296415Sdes                            filterArguments(opL2, 0, add13));
123238106Sdes        assertEqualFunction(sequence(opL2, add13_1),
124238106Sdes                            filterArguments(opL2, 1, add13));
125238106Sdes        System.out.println("[testFilterObject done]");
126238106Sdes    }
127238106Sdes
128238106Sdes    @Test
129238106Sdes    public void testBoxLong() throws Throwable {
130238106Sdes        if (!startTest("testBoxLong"))  return;
131238106Sdes        assertEqualFunction(opJ, opJ.asType(opL_J.type()).asType(opJ.type()));
132238106Sdes    }
133238106Sdes
134238106Sdes    @Test
135238106Sdes    public void testFilterInteger() throws Throwable {
136238106Sdes        if (!startTest("testFilterInteger"))  return;
137238106Sdes        assertEqualFunction(opI, sequence(convI_L, opL_I));
138238106Sdes    }
139238106Sdes
140238106Sdes    @Test
141238106Sdes    public void testIntSpreads() throws Throwable {
142238106Sdes        if (!startTest("testIntSpreads"))  return;
143238106Sdes        MethodHandle id = identity(int[].class);
144238106Sdes        final int MAX = MAX_ARITY-2;  // 253+1 would cause parameter overflow with 'this' added
145238106Sdes        for (int nargs = 0; nargs <= MAX; nargs++) {
146238106Sdes            if (nargs > 30 && nargs < MAX-20)  nargs += 10;
147238106Sdes            int[] args = new int[nargs];
148238106Sdes            for (int j = 0; j < args.length; j++)  args[j] = j + 11;
149238106Sdes            //System.out.println("testIntSpreads "+Arrays.toString(args));
150238106Sdes            int[] args1 = (int[]) id.invokeExact(args);
151238106Sdes            assertArrayEquals(args, args1);
152238106Sdes            MethodHandle coll = id.asCollector(int[].class, nargs);
153238106Sdes            int[] args2 = args;
154238106Sdes            switch (nargs) {
155238106Sdes                case 0:  args2 = (int[]) coll.invokeExact(); break;
156238106Sdes                case 1:  args2 = (int[]) coll.invokeExact(args[0]); break;
157238106Sdes                case 2:  args2 = (int[]) coll.invokeExact(args[0], args[1]); break;
158238106Sdes                case 3:  args2 = (int[]) coll.invokeExact(args[0], args[1], args[2]); break;
159238106Sdes                case 4:  args2 = (int[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
160238106Sdes                case 5:  args2 = (int[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
161238106Sdes            }
162238106Sdes            assertArrayEquals(args, args2);
163238106Sdes            MethodHandle mh = coll.asSpreader(int[].class, nargs);
164238106Sdes            int[] args3 = (int[]) mh.invokeExact(args);
165238106Sdes            assertArrayEquals(args, args3);
166238106Sdes        }
167238106Sdes    }
168238106Sdes
169238106Sdes    @Test
170238106Sdes    public void testByteSpreads() throws Throwable {
171238106Sdes        if (!startTest("testByteSpreads"))  return;
172238106Sdes        MethodHandle id = identity(byte[].class);
173238106Sdes        final int MAX = MAX_ARITY-2;  // 253+1 would cause parameter overflow with 'this' added
174238106Sdes        for (int nargs = 0; nargs <= MAX; nargs++) {
175238106Sdes            if (nargs > 30 && nargs < MAX-20)  nargs += 10;
176238106Sdes            byte[] args = new byte[nargs];
177238106Sdes            for (int j = 0; j < args.length; j++)  args[j] = (byte)(j + 11);
178238106Sdes            //System.out.println("testByteSpreads "+Arrays.toString(args));
179238106Sdes            byte[] args1 = (byte[]) id.invokeExact(args);
180238106Sdes            assertArrayEquals(args, args1);
181238106Sdes            MethodHandle coll = id.asCollector(byte[].class, nargs);
182238106Sdes            byte[] args2 = args;
183238106Sdes            switch (nargs) {
184238106Sdes                case 0:  args2 = (byte[]) coll.invokeExact(); break;
185238106Sdes                case 1:  args2 = (byte[]) coll.invokeExact(args[0]); break;
186238106Sdes                case 2:  args2 = (byte[]) coll.invokeExact(args[0], args[1]); break;
187238106Sdes                case 3:  args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2]); break;
188238106Sdes                case 4:  args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
189238106Sdes                case 5:  args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
190238106Sdes            }
191238106Sdes            assertArrayEquals(args, args2);
192238106Sdes            MethodHandle mh = coll.asSpreader(byte[].class, nargs);
193238106Sdes            byte[] args3 = (byte[]) mh.invokeExact(args);
194238106Sdes            assertArrayEquals(args, args3);
195238106Sdes        }
196238106Sdes    }
197238106Sdes
198238106Sdes    @Test
199238106Sdes    public void testLongSpreads() throws Throwable {
200238106Sdes        if (!startTest("testLongSpreads"))  return;
201238106Sdes        MethodHandle id = identity(long[].class);
202238106Sdes        final int MAX = (MAX_ARITY - 2) / 2;  // 253/2+1 would cause parameter overflow with 'this' added
203238106Sdes        for (int nargs = 0; nargs <= MAX; nargs++) {
204238106Sdes            if (nargs > 30 && nargs < MAX-20)  nargs += 10;
205238106Sdes            long[] args = new long[nargs];
206238106Sdes            for (int j = 0; j < args.length; j++)  args[j] = (long)(j + 11);
207238106Sdes            //System.out.println("testLongSpreads "+Arrays.toString(args));
208238106Sdes            long[] args1 = (long[]) id.invokeExact(args);
209238106Sdes            assertArrayEquals(args, args1);
210238106Sdes            MethodHandle coll = id.asCollector(long[].class, nargs);
211238106Sdes            long[] args2 = args;
212238106Sdes            switch (nargs) {
213238106Sdes                case 0:  args2 = (long[]) coll.invokeExact(); break;
214238106Sdes                case 1:  args2 = (long[]) coll.invokeExact(args[0]); break;
215238106Sdes                case 2:  args2 = (long[]) coll.invokeExact(args[0], args[1]); break;
216238106Sdes                case 3:  args2 = (long[]) coll.invokeExact(args[0], args[1], args[2]); break;
217238106Sdes                case 4:  args2 = (long[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
218238106Sdes                case 5:  args2 = (long[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
219238106Sdes            }
220238106Sdes            assertArrayEquals(args, args2);
221238106Sdes            MethodHandle mh = coll.asSpreader(long[].class, nargs);
222238106Sdes            long[] args3 = (long[]) mh.invokeExact(args);
223238106Sdes            assertArrayEquals(args, args3);
224238106Sdes        }
225238106Sdes    }
226238106Sdes
227238106Sdes    @Test
228238106Sdes    public void testIntCollects() throws Throwable {
229238106Sdes        if (!startTest("testIntCollects"))  return;
230238106Sdes        for (MethodHandle lister : INT_LISTERS) {
231238106Sdes            int outputs = lister.type().parameterCount();
232238106Sdes            for (int collects = 0; collects <= Math.min(outputs, INT_COLLECTORS.length-1); collects++) {
233238106Sdes                int inputs = outputs - 1 + collects;
234238106Sdes                if (inputs < 0)  continue;
235238106Sdes                for (int pos = 0; pos + collects <= inputs; pos++) {
236238106Sdes                    MethodHandle collector = INT_COLLECTORS[collects];
237238106Sdes                    int[] args = new int[inputs];
238238106Sdes                    int ap = 0, arg = 31;
239238106Sdes                    for (int i = 0; i < pos; i++)
240238106Sdes                        args[ap++] = arg++ + 0;
241238106Sdes                    for (int i = 0; i < collects; i++)
242238106Sdes                        args[ap++] = arg++ + 10;
243238106Sdes                    while (ap < args.length)
244238106Sdes                        args[ap++] = arg++ + 20;
245238106Sdes                    // calculate piecemeal:
246238106Sdes                    //System.out.println("testIntCollects "+Arrays.asList(lister, pos, collector)+" on "+Arrays.toString(args));
247238106Sdes                    int[] collargs = Arrays.copyOfRange(args, pos, pos+collects);
248238106Sdes                    int coll = (int) collector.asSpreader(int[].class, collargs.length).invokeExact(collargs);
249238106Sdes                    int[] listargs = Arrays.copyOfRange(args, 0, outputs);
250238106Sdes                    System.arraycopy(args, pos+collects, listargs, pos+1, outputs - (pos+1));
251238106Sdes                    listargs[pos] = coll;
252238106Sdes                    //System.out.println("  coll="+coll+" listargs="+Arrays.toString(listargs));
253238106Sdes                    Object expect = lister.asSpreader(int[].class, listargs.length).invokeExact(listargs);
254238106Sdes                    //System.out.println("  expect="+expect);
255238106Sdes
256238106Sdes                    // now use the combined MH, and test the output:
257238106Sdes                    MethodHandle mh = collectArguments(lister, pos, int[].class, INT_COLLECTORS[collects]);
258238106Sdes                    if (mh == null)  continue;  // no infix collection, yet
259238106Sdes                    assert(mh.type().parameterCount() == inputs);
260238106Sdes                    Object observe = mh.asSpreader(int[].class, args.length).invokeExact(args);
261238106Sdes                    assertEquals(expect, observe);
262238106Sdes                }
263238106Sdes            }
264238106Sdes        }
265238106Sdes    }
266238106Sdes
267238106Sdes    @Test
268238106Sdes    public void testByteCollects() throws Throwable {
269238106Sdes        if (!startTest("testByteCollects"))  return;
270238106Sdes        for (MethodHandle lister : BYTE_LISTERS) {
271238106Sdes            int outputs = lister.type().parameterCount();
272238106Sdes            for (int collects = 0; collects <= Math.min(outputs, BYTE_COLLECTORS.length-1); collects++) {
273238106Sdes                int inputs = outputs - 1 + collects;
274238106Sdes                if (inputs < 0)  continue;
275238106Sdes                for (int pos = 0; pos + collects <= inputs; pos++) {
276238106Sdes                    MethodHandle collector = BYTE_COLLECTORS[collects];
277238106Sdes                    byte[] args = new byte[inputs];
278238106Sdes                    int ap = 0, arg = 31;
279238106Sdes                    for (int i = 0; i < pos; i++)
280238106Sdes                        args[ap++] = (byte)(arg++ + 0);
281238106Sdes                    for (int i = 0; i < collects; i++)
282238106Sdes                        args[ap++] = (byte)(arg++ + 10);
283238106Sdes                    while (ap < args.length)
284238106Sdes                        args[ap++] = (byte)(arg++ + 20);
285238106Sdes                    // calculate piecemeal:
286238106Sdes                    //System.out.println("testIntCollects "+Arrays.asList(lister, pos, collector)+" on "+Arrays.toString(args));
287238106Sdes                    byte[] collargs = Arrays.copyOfRange(args, pos, pos+collects);
288238106Sdes                    byte coll = (byte) collector.asSpreader(byte[].class, collargs.length).invokeExact(collargs);
289238106Sdes                    byte[] listargs = Arrays.copyOfRange(args, 0, outputs);
290238106Sdes                    System.arraycopy(args, pos+collects, listargs, pos+1, outputs - (pos+1));
291238106Sdes                    listargs[pos] = coll;
292238106Sdes                    //System.out.println("  coll="+coll+" listargs="+Arrays.toString(listargs));
293238106Sdes                    Object expect = lister.asSpreader(byte[].class, listargs.length).invokeExact(listargs);
294238106Sdes                    //System.out.println("  expect="+expect);
295238106Sdes
296238106Sdes                    // now use the combined MH, and test the output:
297238106Sdes                    MethodHandle mh = collectArguments(lister, pos, byte[].class, BYTE_COLLECTORS[collects]);
298238106Sdes                    if (mh == null)  continue;  // no infix collection, yet
299238106Sdes                    assert(mh.type().parameterCount() == inputs);
300238106Sdes                    Object observe = mh.asSpreader(byte[].class, args.length).invokeExact(args);
301238106Sdes                    assertEquals(expect, observe);
302238106Sdes                }
303238106Sdes            }
304238106Sdes        }
305238106Sdes    }
306238106Sdes
307238106Sdes    private static MethodHandle collectArguments(MethodHandle lister, int pos, Class<?> array, MethodHandle collector) {
308238106Sdes        int collects = collector.type().parameterCount();
309238106Sdes        int outputs = lister.type().parameterCount();
310238106Sdes        if (pos == outputs - 1)
311238106Sdes            return MethodHandles.filterArguments(lister, pos,
312238106Sdes                        collector.asSpreader(array, collects))
313238106Sdes                            .asCollector(array, collects);
314238106Sdes        //return MethodHandles.collectArguments(lister, pos, collector); //no such animal
315238106Sdes        return null;
316238106Sdes    }
317238106Sdes
318238106Sdes    private static final Class<?>[] RETURN_TYPES = {
319238106Sdes        Object.class, String.class, Integer.class,
320238106Sdes        int.class, long.class,
321238106Sdes        boolean.class, byte.class, char.class, short.class,
322238106Sdes        float.class, double.class,
323238106Sdes        void.class,
324238106Sdes    };
325238106Sdes
326238106Sdes    @Test
327238106Sdes    public void testReturns() throws Throwable {
328238106Sdes        if (!startTest("testReturns"))  return;
329238106Sdes        // fault injection:
330238106Sdes        int faultCount = 0;  // total of 1296 tests
331238106Sdes        faultCount = Integer.getInteger("testReturns.faultCount", 0);
332238106Sdes        for (Class<?> ret : RETURN_TYPES) {
333238106Sdes            // make a complicated identity function and pass something through it
334238106Sdes            System.out.println(ret.getSimpleName());
335238106Sdes            Class<?> vret = (ret == void.class) ? Void.class : ret;
336238106Sdes            MethodHandle id = // (vret)->ret
337238106Sdes                identity(vret).asType(methodType(ret, vret));
338238106Sdes            final int LENGTH = 4;
339238106Sdes            int[] index = {0};
340238106Sdes            Object vals = java.lang.reflect.Array.newInstance(vret, LENGTH);
341238106Sdes            MethodHandle indexGetter =  //()->int
342238106Sdes                insertArguments(arrayElementGetter(index.getClass()), 0, index, 0);
343238106Sdes            MethodHandle valSelector =  // (int)->vret
344238106Sdes                arrayElementGetter(vals.getClass()).bindTo(vals);
345238106Sdes            MethodHandle valGetter =  // ()->vret
346238106Sdes                foldArguments(valSelector, indexGetter);
347238106Sdes            if (ret != void.class) {
348238106Sdes                for (int i = 0; i < LENGTH; i++) {
349238106Sdes                    Object val = (i + 50);
350238106Sdes                    if (ret == boolean.class)  val = (i % 3 == 0);
351238106Sdes                    if (ret == String.class)   val = "#"+i;
352238106Sdes                    if (ret == char.class)     val = (char)('a'+i);
353238106Sdes                    if (ret == byte.class)     val = (byte)~i;
354238106Sdes                    if (ret == short.class)    val = (short)(1<<i);
355238106Sdes                    java.lang.reflect.Array.set(vals, i, val);
356238106Sdes                }
357238106Sdes            }
358238106Sdes            for (int i = 0; i < LENGTH; i++) {
359238106Sdes                Object val = java.lang.reflect.Array.get(vals, i);
360238106Sdes                System.out.println(i+" => "+val);
361249141Sdes                index[0] = i;
362238106Sdes                if (--faultCount == 0)  index[0] ^= 1;
363238106Sdes                Object x = valGetter.invokeWithArguments();
364238106Sdes                assertEquals(val, x);
365238106Sdes                // make a return-filter call:  x = id(valGetter())
366238106Sdes                if (--faultCount == 0)  index[0] ^= 1;
367238106Sdes                x = filterReturnValue(valGetter, id).invokeWithArguments();
368238106Sdes                assertEquals(val, x);
369266114Sdes                // make a filter call:  x = id(*,valGetter(),*)
370238106Sdes                for (int len = 1; len <= 4; len++) {
371238106Sdes                    for (int pos = 0; pos < len; pos++) {
372238106Sdes                        MethodHandle proj = id;  // lambda(..., vret x,...){x}
373238106Sdes                        for (int j = 0; j < len; j++) {
374238106Sdes                            if (j == pos)  continue;
375238106Sdes                            proj = dropArguments(proj, j, Object.class);
376238106Sdes                        }
377238106Sdes                        assert(proj.type().parameterCount() == len);
378238106Sdes                        // proj: (Object*, pos: vret, Object*)->ret
379238106Sdes                        assertEquals(vret, proj.type().parameterType(pos));
380238106Sdes                        MethodHandle vgFilter = dropArguments(valGetter, 0, Object.class);
381238106Sdes                        if (--faultCount == 0)  index[0] ^= 1;
382238106Sdes                        x = filterArguments(proj, pos, vgFilter).invokeWithArguments(new Object[len]);
383238106Sdes                        assertEquals(val, x);
384238106Sdes                    }
385238106Sdes                }
386238106Sdes                // make a fold call:
387238106Sdes                for (int len = 0; len <= 4; len++) {
388238106Sdes                    for (int fold = 0; fold <= len; fold++) {
389238106Sdes                        MethodHandle proj = id;  // lambda(ret x, ...){x}
390238106Sdes                        if (ret == void.class)  proj = constant(Object.class, null);
391238106Sdes                        int arg0 = (ret == void.class ? 0 : 1);
392238106Sdes                        for (int j = 0; j < len; j++) {
393238106Sdes                            proj = dropArguments(proj, arg0, Object.class);
394238106Sdes                        }
395238106Sdes                        assert(proj.type().parameterCount() == arg0 + len);
396238106Sdes                        // proj: (Object*, pos: vret, Object*)->ret
397238106Sdes                        if (arg0 != 0)  assertEquals(vret, proj.type().parameterType(0));
398238106Sdes                        MethodHandle vgFilter = valGetter.asType(methodType(ret));
399238106Sdes                        for (int j = 0; j < fold; j++) {
400238106Sdes                            vgFilter = dropArguments(vgFilter, j, Object.class);
401238106Sdes                        }
402238106Sdes                        x = foldArguments(proj, vgFilter).invokeWithArguments(new Object[len]);
403238106Sdes                        if (--faultCount == 0)  index[0] ^= 1;
404238106Sdes                        assertEquals(val, x);
405238106Sdes                    }
406238106Sdes                }
407238106Sdes            }
408238106Sdes        }
409238106Sdes        //System.out.println("faultCount="+faultCount);
410238106Sdes    }
411238106Sdes
412238106Sdes    @Test
413238106Sdes    public void testRecursion() throws Throwable {
414238106Sdes        if (!startTest("testRecursion"))  return;
415238106Sdes        final int LIMIT = 10;
416238106Sdes        for (int i = 0; i < LIMIT; i++) {
417238106Sdes            RFCB rfcb = new RFCB(i);
418238106Sdes            Object x = "x", y = "y";
419238106Sdes            Object result = rfcb.recursiveFunction(x, y);
420238106Sdes            verbose(1, result);
421238106Sdes        }
422238106Sdes    }
423238106Sdes    /** Recursive Function Control Block */
424238106Sdes    private static class RFCB {
425238106Sdes        java.util.Random random;
426238106Sdes        final MethodHandle[] fns;
427238106Sdes        int depth;
428238106Sdes        @SuppressWarnings("LeakingThisInConstructor")
429238106Sdes        RFCB(int seed) throws Throwable {
430238106Sdes            this.random = new java.util.Random(seed);
431238106Sdes            this.fns = new MethodHandle[Math.max(29, (1 << MAX_DEPTH-2)/3)];
432238106Sdes            java.util.Arrays.fill(fns, lookup().bind(this, "recursiveFunction", genericMethodType(2)));
433238106Sdes            for (int i = 5; i < fns.length; i++) {
434238106Sdes                switch (i % 4) {
435238106Sdes                case 0: fns[i] = filterArguments(fns[i - 5], 0, insertArguments(fns[i - 4], 1, ".")); break;
436238106Sdes                case 1: fns[i] = filterArguments(fns[i - 5], 1, insertArguments(fns[i - 3], 1, ".")); break;
437238106Sdes                case 2: fns[i] = filterReturnValue(fns[i - 5], insertArguments(fns[i - 2], 1, ".")); break;
438238106Sdes                }
439238106Sdes            }
440238106Sdes        }
441238106Sdes        Object recursiveFunction(Object x, Object y) throws Throwable {
442238106Sdes            depth++;
443238106Sdes            try {
444238106Sdes                final int ACTION_COUNT = 11;
445238106Sdes                switch (random.nextInt(ACTION_COUNT)) {
446238106Sdes                case 1:
447238106Sdes                    Throwable ex = new RuntimeException();
448238106Sdes                    ex.fillInStackTrace();
449238106Sdes                    if (VERBOSITY >= 2) ex.printStackTrace(System.out);
450238106Sdes                    x = "ST; " + x;
451238106Sdes                    break;
452238106Sdes                case 2:
453238106Sdes                    System.gc();
454238106Sdes                    x = "GC; " + x;
455238106Sdes                    break;
456238106Sdes                }
457238106Sdes                boolean isLeaf = (depth >= MAX_DEPTH);
458238106Sdes                if (isLeaf) {
459238106Sdes                    return Arrays.asList(x, y).toString();
460238106Sdes                }
461238106Sdes                return fns[random.nextInt(fns.length)].invokeExact(x, y);
462238106Sdes            } finally {
463238106Sdes                depth--;
464238106Sdes            }
465238106Sdes        }
466238106Sdes    }
467238106Sdes
468238106Sdes    private static MethodHandle sequence(MethodHandle mh1, MethodHandle... mhs) {
469238106Sdes        MethodHandle res = mh1;
470238106Sdes        for (MethodHandle mh2 : mhs)
471238106Sdes            res = filterReturnValue(res, mh2);
472238106Sdes        return res;
473238106Sdes    }
474238106Sdes    private static void assertEqualFunction(MethodHandle x, MethodHandle y) throws Throwable {
475238106Sdes        assertEquals(x.type(), y.type()); //??
476238106Sdes        MethodType t = x.type();
477238106Sdes        if (t.parameterCount() == 0) {
478238106Sdes            assertEqualFunctionAt(null, x, y);
479238106Sdes            return;
480238106Sdes        }
481238106Sdes        Class<?> ptype = t.parameterType(0);
482238106Sdes        if (ptype == long.class || ptype == Long.class) {
483238106Sdes            for (long i = -10; i <= 10; i++) {
484238106Sdes                assertEqualFunctionAt(i, x, y);
485238106Sdes            }
486238106Sdes        } else {
487238106Sdes            for (int i = -10; i <= 10; i++) {
488238106Sdes                assertEqualFunctionAt(i, x, y);
489238106Sdes            }
490238106Sdes        }
491238106Sdes    }
492238106Sdes    private static void assertEqualFunctionAt(Object v, MethodHandle x, MethodHandle y) throws Throwable {
493238106Sdes        Object[] args = new Object[x.type().parameterCount()];
494238106Sdes        Arrays.fill(args, v);
495238106Sdes        Object xval = invokeWithCatch(x, args);
496238106Sdes        Object yval = invokeWithCatch(y, args);
497238106Sdes        String msg = "ok";
498238106Sdes        if (!Objects.equals(xval, yval)) {
499238106Sdes            msg = ("applying "+x+" & "+y+" to "+v);
500238106Sdes        }
501238106Sdes        assertEquals(msg, xval, yval);
502238106Sdes    }
503238106Sdes    private static Object invokeWithCatch(MethodHandle mh, Object... args) throws Throwable {
504238106Sdes        try {
505238106Sdes            return mh.invokeWithArguments(args);
506238106Sdes        } catch (Throwable ex) {
507238106Sdes            System.out.println("threw: "+mh+Arrays.asList(args));
508238106Sdes            ex.printStackTrace(System.out);
509238106Sdes            return ex;
510238106Sdes        }
511238106Sdes    }
512238106Sdes
513238106Sdes    private static final Lookup LOOKUP = lookup();
514238106Sdes    private static MethodHandle findStatic(String name,
515238106Sdes                                           Class<?> rtype,
516238106Sdes                                           Class<?>... ptypes) {
517238106Sdes        try {
518238106Sdes            return LOOKUP.findStatic(LOOKUP.lookupClass(), name, methodType(rtype, ptypes));
519238106Sdes        } catch (ReflectiveOperationException ex) {
520238106Sdes            throw new RuntimeException(ex);
521238106Sdes        }
522238106Sdes    }
523238106Sdes    private static MethodHandle findStatic(String name,
524238106Sdes                                           Class<?> rtype,
525238106Sdes                                           List<?> ptypes) {
526238106Sdes        return findStatic(name, rtype, ptypes.toArray(new Class<?>[ptypes.size()]));
527238106Sdes    }
528238106Sdes    static int getProperty(String name, int dflt) {
529238106Sdes        String qual = LOOKUP.lookupClass().getName();
530238106Sdes        String prop = System.getProperty(qual+"."+name);
531238106Sdes        if (prop == null)  prop = System.getProperty(name);
532238106Sdes        if (prop == null)  return dflt;
533238106Sdes        return Integer.parseInt(prop);
534238106Sdes    }
535238106Sdes
536238106Sdes    private static int opI(int... xs) {
537238106Sdes        stress();
538238106Sdes        int base = 100;
539238106Sdes        int z = 0;
540238106Sdes        for (int x : xs) {
541238106Sdes            z = (z * base) + (x % base);
542238106Sdes        }
543238106Sdes        verbose("opI", xs.length, xs, z);
544238106Sdes        return z;
545238106Sdes    }
546238106Sdes    private static int opI2(int x, int y) { return opI(x, y); }  // x*100 + y%100
547238106Sdes    private static int opI3(int x, int y, int z) { return opI(x, y, z); }
548238106Sdes    private static int opI4(int w, int x, int y, int z) { return opI(w, x, y, z); }
549238106Sdes    private static int opI(int x) { return opI2(x, 37); }
550238106Sdes    private static Object opI_L(int x) { return (Object) opI(x); }
551238106Sdes    private static long opJ3(long x, long y, long z) { return (long) opI3((int)x, (int)y, (int)z); }
552238106Sdes    private static long opJ2(long x, long y) { return (long) opI2((int)x, (int)y); }
553238106Sdes    private static long opJ(long x) { return (long) opI((int)x); }
554238106Sdes    private static Object opL2(Object x, Object y) { return (Object) opI2((int)x, (int)y); }
555238106Sdes    private static Object opL(Object x) { return (Object) opI((int)x); }
556238106Sdes    private static int opL2_I(Object x, Object y) { return opI2((int)x, (int)y); }
557238106Sdes    private static int opL_I(Object x) { return opI((int)x); }
558238106Sdes    private static long opL_J(Object x) { return (long) opI((int)x); }
559238106Sdes    private static final MethodHandle opI, opI2, opI3, opI4, opI_L, opJ, opJ2, opJ3, opL2, opL, opL2_I, opL_I, opL_J;
560238106Sdes    static {
561238106Sdes        opI4 = findStatic("opI4", int.class, int.class, int.class, int.class, int.class);
562238106Sdes        opI3 = findStatic("opI3", int.class, int.class, int.class, int.class);
563238106Sdes        opI2 = findStatic("opI2", int.class, int.class, int.class);
564238106Sdes        opI = findStatic("opI", int.class, int.class);
565238106Sdes        opI_L = findStatic("opI_L", Object.class, int.class);
566238106Sdes        opJ = findStatic("opJ", long.class, long.class);
567238106Sdes        opJ2 = findStatic("opJ2", long.class, long.class, long.class);
568238106Sdes        opJ3 = findStatic("opJ3", long.class, long.class, long.class, long.class);
569238106Sdes        opL2 = findStatic("opL2", Object.class, Object.class, Object.class);
570238106Sdes        opL = findStatic("opL", Object.class, Object.class);
571238106Sdes        opL2_I = findStatic("opL2_I", int.class, Object.class, Object.class);
572238106Sdes        opL_I = findStatic("opL_I", int.class, Object.class);
573238106Sdes        opL_J = findStatic("opL_J", long.class, Object.class);
574238106Sdes    }
575238106Sdes    private static final MethodHandle[] INT_COLLECTORS = {
576238106Sdes        constant(int.class, 42), opI, opI2, opI3, opI4
577238106Sdes    };
578238106Sdes    private static final MethodHandle[] BYTE_COLLECTORS = {
579238106Sdes        constant(byte.class, (byte)42), i2b(opI), i2b(opI2), i2b(opI3), i2b(opI4)
580238106Sdes    };
581238106Sdes    private static final MethodHandle[] LONG_COLLECTORS = {
582238106Sdes        constant(long.class, 42), opJ, opJ2, opJ3
583238106Sdes    };
584238106Sdes
585238106Sdes    private static int addI(int x, int y) { stress(); return x+y; }
586238106Sdes    private static Object addL(Object x, Object y) { return addI((int)x, (int)y); }
587238106Sdes    private static final MethodHandle addI, addL;
588238106Sdes    static {
589238106Sdes        addI = findStatic("addI", int.class, int.class, int.class);
590238106Sdes        addL = findStatic("addL", Object.class, Object.class, Object.class);
591238106Sdes    }
592238106Sdes
593238106Sdes    private static Object list8ints(int a, int b, int c, int d, int e, int f, int g, int h) {
594238106Sdes        return Arrays.asList(a, b, c, d, e, f, g, h);
595238106Sdes    }
596238106Sdes    private static Object list8longs(long a, long b, long c, long d, long e, long f, long g, long h) {
597238106Sdes        return Arrays.asList(a, b, c, d, e, f, g, h);
598238106Sdes    }
599238106Sdes    private static final MethodHandle list8ints = findStatic("list8ints", Object.class,
600238106Sdes                                                             Collections.nCopies(8, int.class));
601238106Sdes    private static final MethodHandle list8longs = findStatic("list8longs", Object.class,
602238106Sdes                                                              Collections.nCopies(8, long.class));
603238106Sdes    private static final MethodHandle[] INT_LISTERS, LONG_LISTERS, BYTE_LISTERS;
604238106Sdes    static {
605238106Sdes        int listerCount = list8ints.type().parameterCount() + 1;
606238106Sdes        INT_LISTERS  = new MethodHandle[listerCount];
607238106Sdes        LONG_LISTERS = new MethodHandle[listerCount];
608238106Sdes        BYTE_LISTERS = new MethodHandle[listerCount];
609238106Sdes        MethodHandle lister = list8ints;
610238106Sdes        MethodHandle llister = list8longs;
611238106Sdes        for (int i = listerCount - 1; ; i--) {
612238106Sdes            INT_LISTERS[i] = lister;
613238106Sdes            LONG_LISTERS[i] = llister;
614238106Sdes            BYTE_LISTERS[i] = i2b(lister);
615238106Sdes            if (i == 0)  break;
616238106Sdes            lister  = insertArguments(lister,  i-1, 0);
617238106Sdes            llister = insertArguments(llister, i-1, 0L);
618238106Sdes        }
619238106Sdes    }
620238106Sdes    private static MethodHandle i2b(MethodHandle mh) {
621238106Sdes        return MethodHandles.explicitCastArguments(mh, subst(mh.type(), int.class, byte.class));
622238106Sdes    }
623238106Sdes    private static MethodType subst(MethodType mt, Class<?> from, Class<?> to) {
624238106Sdes        for (int i = 0; i < mt.parameterCount(); i++) {
625238106Sdes            if (mt.parameterType(i) == from)
626238106Sdes                mt = mt.changeParameterType(i, to);
627238106Sdes        }
628238106Sdes        if (mt.returnType() == from)
629238106Sdes            mt = mt.changeReturnType(to);
630238106Sdes        return mt;
631238106Sdes    }
632238106Sdes
633238106Sdes
634238106Sdes    private static Object  convI_L(int     x) { stress(); return (Object)  x; }
635238106Sdes    private static int     convL_I(Object  x) { stress(); return (int)     x; }
636238106Sdes    private static Object  convJ_L(long    x) { stress(); return (Object)  x; }
637238106Sdes    private static long    convL_J(Object  x) { stress(); return (long)    x; }
638238106Sdes    private static int     convJ_I(long    x) { stress(); return (int)     x; }
639238106Sdes    private static long    convI_J(int     x) { stress(); return (long)    x; }
640238106Sdes    private static final MethodHandle convI_L, convL_I, convJ_L, convL_J, convJ_I, convI_J;
641238106Sdes    static {
642238106Sdes        convI_L = findStatic("convI_L", Object.class, int.class);
643238106Sdes        convL_I = findStatic("convL_I", int.class, Object.class);
644238106Sdes        convJ_L = findStatic("convJ_L", Object.class, long.class);
645238106Sdes        convL_J = findStatic("convL_J", long.class, Object.class);
646238106Sdes        convJ_I = findStatic("convJ_I", int.class, long.class);
647238106Sdes        convI_J = findStatic("convI_J", long.class, int.class);
648238106Sdes    }
649238106Sdes
650238106Sdes    // stress modes:
651238106Sdes    private static final int MAX_DEPTH = getProperty("MAX_DEPTH", 5);
652238106Sdes    private static final int REPEAT = getProperty("REPEAT", 0);
653238106Sdes    private static final int STRESS = getProperty("STRESS", 0);
654238106Sdes    private static /*v*/ int STRESS_COUNT;
655238106Sdes    private static final Object[] SINK = new Object[4];
656238106Sdes    private static void stress() {
657238106Sdes        if (STRESS <= 0) return;
658238106Sdes        int count = STRESS + (STRESS_COUNT++ & 0x1);  // non-constant value
659238106Sdes        for (int i = 0; i < count; i++) {
660238106Sdes            SINK[i % SINK.length] = new Object[STRESS + i % (SINK.length + 1)];
661238106Sdes        }
662238106Sdes    }
663238106Sdes
664238106Sdes    // verbosity:
665238106Sdes    private static final int VERBOSITY = getProperty("VERBOSITY", 0) + (REPEAT == 0 ? 0 : -1);
666238106Sdes    private static void verbose(Object a, Object b, Object c, Object d) {
667238106Sdes        if (VERBOSITY <= 0)  return;
668238106Sdes        verbose(1, a, b, c, d);
669238106Sdes    }
670238106Sdes    private static void verbose(Object a, Object b, Object c) {
671238106Sdes        if (VERBOSITY <= 0)  return;
672238106Sdes        verbose(1, a, b, c);
673238106Sdes    }
674238106Sdes    private static void verbose(int level, Object a, Object... bcd) {
675238106Sdes        if (level > VERBOSITY)  return;
676238106Sdes        String m = a.toString();
677238106Sdes        if (bcd != null && bcd.length > 0) {
678238106Sdes            List<Object> l = new ArrayList<>(bcd.length);
679238106Sdes            for (Object x : bcd) {
680238106Sdes                if (x instanceof Object[])  x = Arrays.asList((Object[])x);
681238106Sdes                if (x instanceof int[])     x = Arrays.toString((int[])x);
682238106Sdes                if (x instanceof long[])    x = Arrays.toString((long[])x);
683238106Sdes                l.add(x);
684238106Sdes            }
685238106Sdes            m = m+Arrays.asList(bcd);
686238106Sdes        }
687238106Sdes        System.out.println(m);
688238106Sdes    }
689238106Sdes    String testOnly;
690238106Sdes    String testOnlyTests;
691238106Sdes    private boolean startTest(String name) {
692238106Sdes        if (testOnly != null && !testOnly.contains(name))
693238106Sdes            return false;
694238106Sdes        verbose(0, "["+name+"]");
695238106Sdes        testOnlyTests = (testOnlyTests == null) ? name : testOnlyTests+" "+name;
696238106Sdes        return true;
697238106Sdes    }
698238106Sdes
699238106Sdes}
700238106Sdes