1 /*
   2  * Copyright (c) 2014, 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 import com.oracle.testlibrary.jsr292.Helper;
  25 import java.lang.invoke.MethodHandle;
  26 import java.lang.invoke.MethodHandles;
  27 import java.lang.invoke.MethodType;
  28 import java.lang.reflect.Array;
  29 import java.util.ArrayList;
  30 import java.util.HashMap;
  31 import java.util.List;
  32 import 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  */
  41 public 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         final Class<?>[] CLASSES = {
 614             Object.class,
 615             int.class,
 616             boolean.class,
 617             byte.class,
 618             short.class,
 619             char.class,
 620             long.class,
 621             float.class,
 622             double.class
 623         };
 624         if (arity > Helper.MAX_ARITY) {
 625             throw new IllegalArgumentException(
 626                     String.format("Arity should not exceed %d!", Helper.MAX_ARITY));
 627         }
 628         List<Class<?>> list = Helper.randomClasses(CLASSES, arity);
 629         list = Helper.getParams(list, false, arity);
 630         int i = Helper.RNG.nextInt(CLASSES.length + 1);
 631         Class<?> rtype = i == CLASSES.length ? void.class : CLASSES[i];
 632         return MethodType.methodType(rtype, list);
 633     }
 634 
 635     /**
 636      * Routine used to obtain a method handles of a given type an kind (return
 637      * value).
 638      *
 639      * @param returnType Type of MH return value.
 640      * @param argTypes Types of MH args.
 641      * @param kind Defines whether the obtained MH returns "1" or "2".
 642      * @return Method handle of the given type.
 643      * @throws NoSuchMethodException
 644      * @throws IllegalAccessException
 645      */
 646     private static MethodHandle methodHandleGenerator(Class<?> returnType,
 647             List<Class<?>> argTypes, TestMethods.Kind kind)
 648             throws NoSuchMethodException, IllegalAccessException {
 649         MethodHandle result;
 650         result = kind.getBasicMH(returnType);
 651         return Helper.addTrailingArgs(result, argTypes.size(), argTypes);
 652     }
 653 
 654     /**
 655      * Routine that generates filter method handles to test
 656      * MethodHandles.filterArguments method.
 657      *
 658      * @param inputType Filter's argument type.
 659      * @param returnType Filter's return type.
 660      * @param kind Filter's return value definer.
 661      * @return A filter method handle, that takes one argument.
 662      * @throws NoSuchMethodException
 663      * @throws IllegalAccessException
 664      */
 665     private static MethodHandle filterGenerator(Class<?> inputType, Class<?> returnType,
 666             TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
 667         MethodHandle tmpMH = kind.getBasicMH(returnType);
 668         if (inputType.equals(void.class)) {
 669             return tmpMH;
 670         }
 671         ArrayList<Class<?>> inputTypeList = new ArrayList<>(1);
 672         inputTypeList.add(inputType);
 673         return Helper.addTrailingArgs(tmpMH, 1, inputTypeList);
 674     }
 675 
 676     private static int argSlotsCount(MethodType mt) {
 677         int result = 0;
 678         for (Class cl : mt.parameterArray()) {
 679             if (cl.equals(long.class) || cl.equals(double.class)) {
 680                 result += 2;
 681             } else {
 682                 result++;
 683             }
 684         }
 685         return result;
 686     }
 687 
 688     private static List<Class<?>> reduceArgListToSlotsCount(List<Class<?>> list,
 689             int desiredSlotCount) {
 690         List<Class<?>> result = new ArrayList<>(desiredSlotCount);
 691         int count = 0;
 692         for (Class<?> cl : list) {
 693             if (count >= desiredSlotCount) {
 694                 break;
 695             }
 696             if (cl.equals(long.class) || cl.equals(double.class)) {
 697                 count += 2;
 698             } else {
 699                 count++;
 700             }
 701             result.add(cl);
 702         }
 703         return result;
 704     }
 705 }