1/*
2 * Copyright (c) 2014, 2015, 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
24import com.oracle.testlibrary.jsr292.Helper;
25import java.lang.invoke.MethodHandle;
26import java.lang.invoke.MethodHandles;
27import java.lang.invoke.MethodType;
28import java.lang.reflect.Array;
29import java.util.ArrayList;
30import java.util.HashMap;
31import java.util.List;
32import java.util.Map;
33
34/**
35 * Enumeration containing information about methods from
36 * {@code j.l.i.MethodHandles} class that are used for testing lambda forms
37 * caching.
38 *
39 * @author kshefov
40 */
41public enum TestMethods {
42
43    FOLD_ARGUMENTS("foldArguments") {
44                @Override
45                public Map<String, Object> getTestCaseData() {
46                    Map<String, Object> data = new HashMap<>();
47                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
48                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
49                    data.put("mtTarget", mtTarget);
50                    // Arity after reducing because of long and double take 2 slots.
51                    int realArity = mtTarget.parameterCount();
52                    int modifierMHArgNum = Helper.RNG.nextInt(realArity + 1);
53                    data.put("modifierMHArgNum", modifierMHArgNum);
54                    Class<?> combinerReturnType;
55                    if (realArity == 0) {
56                        combinerReturnType = void.class;
57                    } else {
58                        combinerReturnType = Helper.RNG.nextBoolean() ? void.class : mtTarget.parameterType(0);
59                    }
60                    data.put("combinerReturnType", combinerReturnType);
61                    return data;
62                }
63
64                @Override
65                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
66                    MethodType mtTarget = (MethodType) data.get("mtTarget");
67                    Class<?> combinerReturnType = (Class) data.get("combinerReturnType");
68                    int modifierMHArgNum = (int) data.get("modifierMHArgNum");
69                    MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
70                            mtTarget.parameterList(), kind);
71                    Class<?> rType = mtTarget.returnType();
72                    int combListStart = (combinerReturnType == void.class) ? 0 : 1;
73                    if (modifierMHArgNum < combListStart) {
74                        modifierMHArgNum = combListStart;
75                    }
76                    MethodHandle combiner = TestMethods.methodHandleGenerator(combinerReturnType,
77                            mtTarget.parameterList().subList(combListStart,
78                                    modifierMHArgNum), kind);
79                    return MethodHandles.foldArguments(target, combiner);
80                }
81            },
82    DROP_ARGUMENTS("dropArguments") {
83                @Override
84                public Map<String, Object> getTestCaseData() {
85                    Map<String, Object> data = new HashMap<>();
86                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
87                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
88                    data.put("mtTarget", mtTarget);
89                    // Arity after reducing because of long and double take 2 slots.
90                    int realArity = mtTarget.parameterCount();
91                    int dropArgsPos = Helper.RNG.nextInt(realArity + 1);
92                    data.put("dropArgsPos", dropArgsPos);
93                    MethodType mtDropArgs = TestMethods.randomMethodTypeGenerator(
94                            Helper.RNG.nextInt(super.maxArity - realArity));
95                    data.put("mtDropArgs", mtDropArgs);
96                    return data;
97                }
98
99                @Override
100                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
101                    MethodType mtTarget = (MethodType) data.get("mtTarget");
102                    MethodType mtDropArgs = (MethodType) data.get("mtDropArgs");
103                    int dropArgsPos = (int) data.get("dropArgsPos");
104                    MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
105                            mtTarget.parameterList(), kind);
106                    int mtTgtSlotsCount = TestMethods.argSlotsCount(mtTarget);
107                    int mtDASlotsCount = TestMethods.argSlotsCount(mtDropArgs);
108                    List<Class<?>> fakeParList;
109                    if (mtTgtSlotsCount + mtDASlotsCount > super.maxArity - 1) {
110                        fakeParList = TestMethods.reduceArgListToSlotsCount(mtDropArgs.parameterList(),
111                                super.maxArity - mtTgtSlotsCount - 1);
112                    } else {
113                        fakeParList = mtDropArgs.parameterList();
114                    }
115                    return MethodHandles.dropArguments(target, dropArgsPos, fakeParList);
116                }
117            },
118    EXPLICIT_CAST_ARGUMENTS("explicitCastArguments", Helper.MAX_ARITY / 2) {
119                @Override
120                public Map<String, Object> getTestCaseData() {
121                    Map<String, Object> data = new HashMap<>();
122                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
123                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
124                    data.put("mtTarget", mtTarget);
125                    // Arity after reducing because of long and double take 2 slots.
126                    int realArity = mtTarget.parameterCount();
127                    MethodType mtExcplCastArgs = TestMethods.randomMethodTypeGenerator(realArity);
128                    if (mtTarget.returnType() == void.class) {
129                        mtExcplCastArgs = MethodType.methodType(void.class,
130                                mtExcplCastArgs.parameterArray());
131                    }
132                    if (mtExcplCastArgs.returnType() == void.class) {
133                        mtExcplCastArgs = MethodType.methodType(mtTarget.returnType(),
134                                mtExcplCastArgs.parameterArray());
135                    }
136                    data.put("mtExcplCastArgs", mtExcplCastArgs);
137                    return data;
138                }
139
140                @Override
141                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
142                    MethodType mtTarget = (MethodType) data.get("mtTarget");
143                    MethodType mtExcplCastArgs = (MethodType) data.get("mtExcplCastArgs");
144                    MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
145                            mtTarget.parameterList(), kind);
146                    return MethodHandles.explicitCastArguments(target, mtExcplCastArgs);
147                }
148            },
149    FILTER_ARGUMENTS("filterArguments", Helper.MAX_ARITY / 2) {
150                @Override
151                public Map<String, Object> getTestCaseData() {
152                    Map<String, Object> data = new HashMap<>();
153                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
154                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
155                    data.put("mtTarget", mtTarget);
156                    // Arity after reducing because of long and double take 2 slots.
157                    int realArity = mtTarget.parameterCount();
158                    int filterArgsPos = Helper.RNG.nextInt(realArity + 1);
159                    data.put("filterArgsPos", filterArgsPos);
160                    int filtersArgsArrayLength = Helper.RNG.nextInt(realArity + 1 - filterArgsPos);
161                    data.put("filtersArgsArrayLength", filtersArgsArrayLength);
162                    MethodType mtFilter = TestMethods.randomMethodTypeGenerator(filtersArgsArrayLength);
163                    data.put("mtFilter", mtFilter);
164                    return data;
165                }
166
167                @Override
168                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
169                    MethodType mtTarget = (MethodType) data.get("mtTarget");
170                    MethodType mtFilter = (MethodType) data.get("mtFilter");
171                    int filterArgsPos = (int) data.get("filterArgsPos");
172                    int filtersArgsArrayLength = (int) data.get("filtersArgsArrayLength");
173                    MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
174                            mtTarget.parameterList(), kind);
175                    MethodHandle[] filters = new MethodHandle[filtersArgsArrayLength];
176                    for (int i = 0; i < filtersArgsArrayLength; i++) {
177                        filters[i] = TestMethods.filterGenerator(mtFilter.parameterType(i),
178                                mtTarget.parameterType(filterArgsPos + i), kind);
179                    }
180                    return MethodHandles.filterArguments(target, filterArgsPos, filters);
181                }
182            },
183    FILTER_RETURN_VALUE("filterReturnValue") {
184                @Override
185                public Map<String, Object> getTestCaseData() {
186                    Map<String, Object> data = new HashMap<>();
187                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
188                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
189                    data.put("mtTarget", mtTarget);
190                    // Arity after reducing because of long and double take 2 slots.
191                    int realArity = mtTarget.parameterCount();
192                    int filterArgsPos = Helper.RNG.nextInt(realArity + 1);
193                    int filtersArgsArrayLength = Helper.RNG.nextInt(realArity + 1 - filterArgsPos);
194                    MethodType mtFilter = TestMethods.randomMethodTypeGenerator(filtersArgsArrayLength);
195                    data.put("mtFilter", mtFilter);
196                    return data;
197                }
198
199                @Override
200                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
201                    MethodType mtTarget = (MethodType) data.get("mtTarget");
202                    MethodType mtFilter = (MethodType) data.get("mtFilter");
203                    MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
204                            mtTarget.parameterList(), kind);
205                    MethodHandle filter = TestMethods.filterGenerator(mtTarget.returnType(),
206                            mtFilter.returnType(), kind);
207                    return MethodHandles.filterReturnValue(target, filter);
208                }
209            },
210    INSERT_ARGUMENTS("insertArguments", Helper.MAX_ARITY - 3) {
211                @Override
212                public Map<String, Object> getTestCaseData() {
213                    Map<String, Object> data = new HashMap<>();
214                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
215                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
216                    data.put("mtTarget", mtTarget);
217                    // Arity after reducing because of long and double take 2 slots.
218                    int realArity = mtTarget.parameterCount();
219                    int insertArgsPos = Helper.RNG.nextInt(realArity + 1);
220                    data.put("insertArgsPos", insertArgsPos);
221                    int insertArgsArrayLength = Helper.RNG.nextInt(realArity + 1 - insertArgsPos);
222                    MethodType mtInsertArgs = MethodType.methodType(void.class, mtTarget.parameterList()
223                            .subList(insertArgsPos, insertArgsPos + insertArgsArrayLength));
224                    data.put("mtInsertArgs", mtInsertArgs);
225                    return data;
226                }
227
228                @Override
229                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
230                    MethodType mtTarget = (MethodType) data.get("mtTarget");
231                    MethodType mtInsertArgs = (MethodType) data.get("mtInsertArgs");
232                    int insertArgsPos = (int) data.get("insertArgsPos");
233                    MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
234                            mtTarget.parameterList(), kind);
235                    Object[] insertList = Helper.randomArgs(mtInsertArgs.parameterList());
236                    return MethodHandles.insertArguments(target, insertArgsPos, insertList);
237                }
238            },
239    PERMUTE_ARGUMENTS("permuteArguments", Helper.MAX_ARITY / 2) {
240                @Override
241                public Map<String, Object> getTestCaseData() {
242                    Map<String, Object> data = new HashMap<>();
243                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
244                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
245                    // Arity after reducing because of long and double take 2 slots.
246                    int realArity = mtTarget.parameterCount();
247                    int[] permuteArgsReorderArray = new int[realArity];
248                    int mtPermuteArgsNum = Helper.RNG.nextInt(Helper.MAX_ARITY);
249                    mtPermuteArgsNum = mtPermuteArgsNum == 0 ? 1 : mtPermuteArgsNum;
250                    MethodType mtPermuteArgs = TestMethods.randomMethodTypeGenerator(mtPermuteArgsNum);
251                    mtTarget = mtTarget.changeReturnType(mtPermuteArgs.returnType());
252                    for (int i = 0; i < realArity; i++) {
253                        int mtPermuteArgsParNum = Helper.RNG.nextInt(mtPermuteArgs.parameterCount());
254                        permuteArgsReorderArray[i] = mtPermuteArgsParNum;
255                        mtTarget = mtTarget.changeParameterType(
256                                i, mtPermuteArgs.parameterType(mtPermuteArgsParNum));
257                    }
258                    data.put("mtTarget", mtTarget);
259                    data.put("permuteArgsReorderArray", permuteArgsReorderArray);
260                    data.put("mtPermuteArgs", mtPermuteArgs);
261                    return data;
262                }
263
264                @Override
265                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
266                    MethodType mtTarget = (MethodType) data.get("mtTarget");
267                    MethodType mtPermuteArgs = (MethodType) data.get("mtPermuteArgs");
268                    int[] permuteArgsReorderArray = (int[]) data.get("permuteArgsReorderArray");
269                    MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
270                            mtTarget.parameterList(), kind);
271                    return MethodHandles.permuteArguments(target, mtPermuteArgs, permuteArgsReorderArray);
272                }
273            },
274    THROW_EXCEPTION("throwException") {
275                @Override
276                public Map<String, Object> getTestCaseData() {
277                    Map<String, Object> data = new HashMap<>();
278                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
279                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
280                    data.put("mtTarget", mtTarget);
281                    return data;
282                }
283
284                @Override
285                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
286                    MethodType mtTarget = (MethodType) data.get("mtTarget");
287                    Class<?> rType = mtTarget.returnType();
288                    return MethodHandles.throwException(rType, Exception.class
289                    );
290                }
291            },
292    GUARD_WITH_TEST("guardWithTest") {
293                @Override
294                public Map<String, Object> getTestCaseData() {
295                    Map<String, Object> data = new HashMap<>();
296                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
297                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
298                    data.put("mtTarget", mtTarget);
299                    // Arity after reducing because of long and double take 2 slots.
300                    int realArity = mtTarget.parameterCount();
301                    int modifierMHArgNum = Helper.RNG.nextInt(realArity + 1);
302                    data.put("modifierMHArgNum", modifierMHArgNum);
303                    return data;
304                }
305
306                @Override
307                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
308                    MethodType mtTarget = (MethodType) data.get("mtTarget");
309                    int modifierMHArgNum = (int) data.get("modifierMHArgNum");
310                    TestMethods.Kind targetKind;
311                    TestMethods.Kind fallbackKind;
312                    if (kind.equals(TestMethods.Kind.ONE)) {
313                        targetKind = TestMethods.Kind.ONE;
314                        fallbackKind = TestMethods.Kind.TWO;
315                    } else {
316                        targetKind = TestMethods.Kind.TWO;
317                        fallbackKind = TestMethods.Kind.ONE;
318                    }
319                    MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
320                            mtTarget.parameterList(), targetKind);
321                    MethodHandle fallback = TestMethods.methodHandleGenerator(mtTarget.returnType(),
322                            mtTarget.parameterList(), fallbackKind);
323                    MethodHandle test = TestMethods.methodHandleGenerator(boolean.class,
324                            mtTarget.parameterList().subList(0, modifierMHArgNum), kind);
325                    return MethodHandles.guardWithTest(test, target, fallback);
326                }
327            },
328    CATCH_EXCEPTION("catchException") {
329                @Override
330                public Map<String, Object> getTestCaseData() {
331                    Map<String, Object> data = new HashMap<>();
332                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
333                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
334                    data.put("mtTarget", mtTarget);
335                    // Arity after reducing because of long and double take 2 slots.
336                    int realArity = mtTarget.parameterCount();
337                    int modifierMHArgNum = Helper.RNG.nextInt(realArity + 1);
338                    data.put("modifierMHArgNum", modifierMHArgNum);
339                    return data;
340                }
341
342                @Override
343                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
344                    MethodType mtTarget = (MethodType) data.get("mtTarget");
345                    int modifierMHArgNum = (int) data.get("modifierMHArgNum");
346                    MethodHandle target;
347                    if (kind.equals(TestMethods.Kind.ONE)) {
348                        target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
349                                mtTarget.parameterList(), TestMethods.Kind.ONE);
350                    } else {
351                        target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
352                                mtTarget.parameterList(), TestMethods.Kind.EXCEPT);
353                    }
354                    List<Class<?>> handlerParamList = new ArrayList<>(mtTarget.parameterCount() + 1);
355                    handlerParamList.add(Exception.class);
356                    handlerParamList.addAll(mtTarget.parameterList().subList(0, modifierMHArgNum));
357                    MethodHandle handler = TestMethods.methodHandleGenerator(
358                            mtTarget.returnType(), handlerParamList, TestMethods.Kind.TWO);
359                    return MethodHandles.catchException(target, Exception.class, handler);
360                }
361            },
362    INVOKER("invoker", Helper.MAX_ARITY - 1) {
363                @Override
364                public Map<String, Object> getTestCaseData() {
365                    Map<String, Object> data = new HashMap<>();
366                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
367                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
368                    data.put("mtTarget", mtTarget);
369                    return data;
370                }
371
372                @Override
373                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
374                    MethodType mtTarget = (MethodType) data.get("mtTarget");
375                    return MethodHandles.invoker(mtTarget);
376                }
377            },
378    EXACT_INVOKER("exactInvoker", Helper.MAX_ARITY - 1) {
379                @Override
380                public Map<String, Object> getTestCaseData() {
381                    Map<String, Object> data = new HashMap<>();
382                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
383                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
384                    data.put("mtTarget", mtTarget);
385                    return data;
386                }
387
388                @Override
389                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
390                    MethodType mtTarget = (MethodType) data.get("mtTarget");
391                    return MethodHandles.exactInvoker(mtTarget);
392                }
393            },
394    SPREAD_INVOKER("spreadInvoker", Helper.MAX_ARITY - 1) {
395                @Override
396                public Map<String, Object> getTestCaseData() {
397                    Map<String, Object> data = new HashMap<>();
398                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
399                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
400                    data.put("mtTarget", mtTarget);
401                    // Arity after reducing because of long and double take 2 slots.
402                    int realArity = mtTarget.parameterCount();
403                    int modifierMHArgNum = Helper.RNG.nextInt(realArity + 1);
404                    data.put("modifierMHArgNum", modifierMHArgNum);
405                    return data;
406                }
407
408                @Override
409                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
410                    MethodType mtTarget = (MethodType) data.get("mtTarget");
411                    int modifierMHArgNum = (int) data.get("modifierMHArgNum");
412                    return MethodHandles.spreadInvoker(mtTarget, modifierMHArgNum);
413                }
414            },
415    ARRAY_ELEMENT_GETTER("arrayElementGetter") {
416                @Override
417                public Map<String, Object> getTestCaseData() {
418                    Map<String, Object> data = new HashMap<>();
419                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
420                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
421                    data.put("mtTarget", mtTarget);
422                    return data;
423                }
424
425                @Override
426                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
427                    MethodType mtTarget = (MethodType) data.get("mtTarget");
428                    Class<?> rType = mtTarget.returnType();
429                    if (rType == void.class) {
430                        rType = Object.class;
431                    }
432                    return MethodHandles.arrayElementGetter(Array.newInstance(rType, 2).getClass());
433                }
434            },
435    ARRAY_ELEMENT_SETTER("arrayElementSetter") {
436                @Override
437                public Map<String, Object> getTestCaseData() {
438                    Map<String, Object> data = new HashMap<>();
439                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
440                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
441                    data.put("mtTarget", mtTarget);
442                    return data;
443                }
444
445                @Override
446                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
447                    MethodType mtTarget = (MethodType) data.get("mtTarget");
448                    Class<?> rType = mtTarget.returnType();
449                    if (rType == void.class) {
450                        rType = Object.class;
451                    }
452                    return MethodHandles.arrayElementSetter(Array.newInstance(rType, 2).getClass());
453                }
454            },
455    CONSTANT("constant") {
456                @Override
457                public Map<String, Object> getTestCaseData() {
458                    Map<String, Object> data = new HashMap<>();
459                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
460                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
461                    data.put("mtTarget", mtTarget);
462                    return data;
463                }
464
465                @Override
466                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
467                    MethodType mtTarget = (MethodType) data.get("mtTarget");
468                    Class<?> rType = mtTarget.returnType();
469                    if (rType == void.class) {
470                        rType = Object.class;
471                    }
472                    if (rType.equals(boolean.class)) {
473                        // There should be the same return values because for default values there are special "zero" forms
474                        return MethodHandles.constant(rType, true);
475                    } else {
476                        return MethodHandles.constant(rType, kind.getValue(rType));
477                    }
478                }
479            },
480    IDENTITY("identity") {
481                @Override
482                public Map<String, Object> getTestCaseData() {
483                    Map<String, Object> data = new HashMap<>();
484                    int desiredArity = Helper.RNG.nextInt(super.maxArity);
485                    MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
486                    data.put("mtTarget", mtTarget);
487                    return data;
488                }
489
490                @Override
491                protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
492                    MethodType mtTarget = (MethodType) data.get("mtTarget");
493                    Class<?> rType = mtTarget.returnType();
494                    if (rType == void.class) {
495                        rType = Object.class;
496                    }
497                    return MethodHandles.identity(rType);
498                }
499            };
500
501    /**
502     * Test method's name.
503     */
504    public final String name;
505
506    private final int maxArity;
507
508    private TestMethods(String name, int maxArity) {
509        this.name = name;
510        this.maxArity = maxArity;
511    }
512
513    private TestMethods(String name) {
514        this(name, Helper.MAX_ARITY);
515    }
516
517    protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
518        throw new UnsupportedOperationException("TESTBUG: getMH method is not implemented for test method " + this);
519    }
520
521    /**
522     * Creates an adapter method handle depending on a test method from
523     * MethodHandles class. Adapter is what is returned by the test method. This
524     * method is able to create two kinds of adapters, their type will be the
525     * same, but return values are different.
526     *
527     * @param data a Map containing data to create a method handle, can be
528     * obtained by {@link #getTestCaseData} method
529     * @param kind defines whether adapter ONE or adapter TWO will be
530     * initialized. Should be equal to TestMethods.Kind.ONE or
531     * TestMethods.Kind.TWO
532     * @return Method handle adapter that behaves according to
533     * TestMethods.Kind.ONE or TestMethods.Kind.TWO
534     * @throws java.lang.NoSuchMethodException
535     * @throws java.lang.IllegalAccessException
536     */
537    public MethodHandle getTestCaseMH(Map<String, Object> data, TestMethods.Kind kind)
538            throws NoSuchMethodException, IllegalAccessException {
539        if (data == null) {
540            throw new Error(String.format("TESTBUG: Data for test method %s is not prepared",
541                    this.name));
542        }
543        if (!kind.equals(TestMethods.Kind.ONE) && !kind.equals(TestMethods.Kind.TWO)) {
544            throw new IllegalArgumentException("TESTBUG: Wrong \"kind\" (" + kind
545                    + ") arg to getTestCaseMH function."
546                    + " Should be Kind.ONE or Kind.TWO");
547        }
548        return getMH(data, kind);
549    }
550
551    /**
552     * Returns a data Map needed for {@link #getTestCaseMH} method.
553     *
554     * @return data Map needed for {@link #getTestCaseMH} method
555     */
556    public Map<String, Object> getTestCaseData() {
557        throw new UnsupportedOperationException(
558                "TESTBUG: getTestCaseData method is not implemented for test method " + this);
559    }
560
561    /**
562     * Enumeration used in methodHandleGenerator to define whether a MH returned
563     * by this method returns "2" in different type representations, "4", or
564     * throw an Exception.
565     */
566    public static enum Kind {
567
568        ONE(2),
569        TWO(4),
570        EXCEPT(0);
571
572        private final int value;
573
574        private Object getValue(Class<?> cl) {
575            return Helper.castToWrapper(value, cl);
576        }
577
578        private MethodHandle getBasicMH(Class<?> rType) throws NoSuchMethodException, IllegalAccessException {
579            MethodHandle result = null;
580            switch (this) {
581                case ONE:
582                case TWO:
583                    if (rType.equals(void.class)) {
584                        result = MethodHandles.lookup().findVirtual(Kind.class, "returnVoid", MethodType.methodType(void.class));
585                        result = MethodHandles.insertArguments(result, 0, this);
586                    } else {
587                        result = MethodHandles.constant(rType, getValue(rType));
588                    }
589                    break;
590                case EXCEPT:
591                    result = MethodHandles.throwException(rType, Exception.class);
592                    result = MethodHandles.insertArguments(result, 0, new Exception());
593                    break;
594            }
595            return result;
596        }
597
598        private void returnVoid() {
599        }
600
601        private Kind(int value) {
602            this.value = value;
603        }
604    }
605
606    /**
607     * Routine used to obtain a randomly generated method type.
608     *
609     * @param arity Arity of returned method type.
610     * @return MethodType generated randomly.
611     */
612    private static MethodType randomMethodTypeGenerator(int arity) {
613        return Helper.randomMethodTypeGenerator(arity);
614    }
615
616    /**
617     * Routine used to obtain a method handles of a given type an kind (return
618     * value).
619     *
620     * @param returnType Type of MH return value.
621     * @param argTypes Types of MH args.
622     * @param kind Defines whether the obtained MH returns "1" or "2".
623     * @return Method handle of the given type.
624     * @throws NoSuchMethodException
625     * @throws IllegalAccessException
626     */
627    private static MethodHandle methodHandleGenerator(Class<?> returnType,
628            List<Class<?>> argTypes, TestMethods.Kind kind)
629            throws NoSuchMethodException, IllegalAccessException {
630        MethodHandle result;
631        result = kind.getBasicMH(returnType);
632        return Helper.addTrailingArgs(result, argTypes.size(), argTypes);
633    }
634
635    /**
636     * Routine that generates filter method handles to test
637     * MethodHandles.filterArguments method.
638     *
639     * @param inputType Filter's argument type.
640     * @param returnType Filter's return type.
641     * @param kind Filter's return value definer.
642     * @return A filter method handle, that takes one argument.
643     * @throws NoSuchMethodException
644     * @throws IllegalAccessException
645     */
646    private static MethodHandle filterGenerator(Class<?> inputType, Class<?> returnType,
647            TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
648        MethodHandle tmpMH = kind.getBasicMH(returnType);
649        if (inputType.equals(void.class)) {
650            return tmpMH;
651        }
652        ArrayList<Class<?>> inputTypeList = new ArrayList<>(1);
653        inputTypeList.add(inputType);
654        return Helper.addTrailingArgs(tmpMH, 1, inputTypeList);
655    }
656
657    private static int argSlotsCount(MethodType mt) {
658        int result = 0;
659        for (Class cl : mt.parameterArray()) {
660            if (cl.equals(long.class) || cl.equals(double.class)) {
661                result += 2;
662            } else {
663                result++;
664            }
665        }
666        return result;
667    }
668
669    private static List<Class<?>> reduceArgListToSlotsCount(List<Class<?>> list,
670            int desiredSlotCount) {
671        List<Class<?>> result = new ArrayList<>(desiredSlotCount);
672        int count = 0;
673        for (Class<?> cl : list) {
674            if (count >= desiredSlotCount) {
675                break;
676            }
677            if (cl.equals(long.class) || cl.equals(double.class)) {
678                count += 2;
679            } else {
680                count++;
681            }
682            result.add(cl);
683        }
684        return result;
685    }
686}
687