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(Helper.MAX_ARITY);
  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(Helper.MAX_ARITY);
  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(Helper.MAX_ARITY - 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 > Helper.MAX_ARITY - 1) {
 110                         fakeParList = TestMethods.reduceArgListToSlotsCount(mtDropArgs.parameterList(),
 111                                 Helper.MAX_ARITY - mtTgtSlotsCount - 1);
 112                     } else {
 113                         fakeParList = mtDropArgs.parameterList();
 114                     }
 115                     return MethodHandles.dropArguments(target, dropArgsPos, fakeParList);
 116                 }
 117             },
 118     EXPLICIT_CAST_ARGUMENTS("explicitCastArguments") {
 119                 @Override
 120                 public Map<String, Object> getTestCaseData() {
 121                     Map<String, Object> data = new HashMap<>();
 122                     int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY / 2);
 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") {
 150                 @Override
 151                 public Map<String, Object> getTestCaseData() {
 152                     Map<String, Object> data = new HashMap<>();
 153                     int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY / 2);
 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(Helper.MAX_ARITY);
 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") {
 211                 @Override
 212                 public Map<String, Object> getTestCaseData() {
 213                     Map<String, Object> data = new HashMap<>();
 214                     int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY);
 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") {
 240                 @Override
 241                 public Map<String, Object> getTestCaseData() {
 242                     Map<String, Object> data = new HashMap<>();
 243                     int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY / 2);
 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 mtParmuteArgsNum = Helper.RNG.nextInt(Helper.MAX_ARITY);
 249                     mtParmuteArgsNum = mtParmuteArgsNum == 0 ? 1 : mtParmuteArgsNum;
 250                     MethodType mtPermuteArgs = TestMethods.randomMethodTypeGenerator(mtParmuteArgsNum);
 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(Helper.MAX_ARITY);
 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(Helper.MAX_ARITY);
 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(Helper.MAX_ARITY);
 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") {
 363                 @Override
 364                 public Map<String, Object> getTestCaseData() {
 365                     Map<String, Object> data = new HashMap<>();
 366                     int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY);
 367                     desiredArity = desiredArity <= 252 ? desiredArity : 252;
 368                     MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
 369                     data.put("mtTarget", mtTarget);
 370                     return data;
 371                 }
 372 
 373                 @Override
 374                 protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
 375                     MethodType mtTarget = (MethodType) data.get("mtTarget");
 376                     return MethodHandles.invoker(mtTarget);
 377                 }
 378             },
 379     EXACT_INVOKER("exactInvoker") {
 380                 @Override
 381                 public Map<String, Object> getTestCaseData() {
 382                     Map<String, Object> data = new HashMap<>();
 383                     int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY);
 384                     desiredArity = desiredArity <= 252 ? desiredArity : 252;
 385                     MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
 386                     data.put("mtTarget", mtTarget);
 387                     return data;
 388                 }
 389 
 390                 @Override
 391                 protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
 392                     MethodType mtTarget = (MethodType) data.get("mtTarget");
 393                     return MethodHandles.exactInvoker(mtTarget);
 394                 }
 395             },
 396     SPREAD_INVOKER("spreadInvoker") {
 397                 @Override
 398                 public Map<String, Object> getTestCaseData() {
 399                     Map<String, Object> data = new HashMap<>();
 400                     int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY);
 401                     desiredArity = desiredArity <= 252 ? desiredArity : 252;
 402                     MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
 403                     data.put("mtTarget", mtTarget);
 404                     // Arity after reducing because of long and double take 2 slots.
 405                     int realArity = mtTarget.parameterCount();
 406                     int modifierMHArgNum = Helper.RNG.nextInt(realArity + 1);
 407                     data.put("modifierMHArgNum", modifierMHArgNum);
 408                     return data;
 409                 }
 410 
 411                 @Override
 412                 protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
 413                     MethodType mtTarget = (MethodType) data.get("mtTarget");
 414                     int modifierMHArgNum = (int) data.get("modifierMHArgNum");
 415                     return MethodHandles.spreadInvoker(mtTarget, modifierMHArgNum);
 416                 }
 417             },
 418     ARRAY_ELEMENT_GETTER("arrayElementGetter") {
 419                 @Override
 420                 public Map<String, Object> getTestCaseData() {
 421                     Map<String, Object> data = new HashMap<>();
 422                     int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY);
 423                     MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
 424                     data.put("mtTarget", mtTarget);
 425                     return data;
 426                 }
 427 
 428                 @Override
 429                 protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
 430                     MethodType mtTarget = (MethodType) data.get("mtTarget");
 431                     Class<?> rType = mtTarget.returnType();
 432                     if (rType == void.class) {
 433                         rType = Object.class;
 434                     }
 435                     return MethodHandles.arrayElementGetter(Array.newInstance(rType, 2).getClass());
 436                 }
 437             },
 438     ARRAY_ELEMENT_SETTER("arrayElementSetter") {
 439                 @Override
 440                 public Map<String, Object> getTestCaseData() {
 441                     Map<String, Object> data = new HashMap<>();
 442                     int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY);
 443                     MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
 444                     data.put("mtTarget", mtTarget);
 445                     return data;
 446                 }
 447 
 448                 @Override
 449                 protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
 450                     MethodType mtTarget = (MethodType) data.get("mtTarget");
 451                     Class<?> rType = mtTarget.returnType();
 452                     if (rType == void.class) {
 453                         rType = Object.class;
 454                     }
 455                     return MethodHandles.arrayElementSetter(Array.newInstance(rType, 2).getClass());
 456                 }
 457             },
 458     CONSTANT("constant") {
 459                 @Override
 460                 public Map<String, Object> getTestCaseData() {
 461                     Map<String, Object> data = new HashMap<>();
 462                     int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY);
 463                     MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
 464                     data.put("mtTarget", mtTarget);
 465                     return data;
 466                 }
 467 
 468                 @Override
 469                 protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
 470                     MethodType mtTarget = (MethodType) data.get("mtTarget");
 471                     Class<?> rType = mtTarget.returnType();
 472                     if (rType == void.class) {
 473                         rType = Object.class;
 474                     }
 475                     if (rType.equals(boolean.class)) {
 476                         // There should be the same return values because for default values there are special "zero" forms
 477                         return MethodHandles.constant(rType, true);
 478                     } else {
 479                         return MethodHandles.constant(rType, kind.getValue(rType));
 480                     }
 481                 }
 482             },
 483     IDENTITY("identity") {
 484                 @Override
 485                 public Map<String, Object> getTestCaseData() {
 486                     Map<String, Object> data = new HashMap<>();
 487                     int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY);
 488                     MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
 489                     data.put("mtTarget", mtTarget);
 490                     return data;
 491                 }
 492 
 493                 @Override
 494                 protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
 495                     MethodType mtTarget = (MethodType) data.get("mtTarget");
 496                     Class<?> rType = mtTarget.returnType();
 497                     if (rType == void.class) {
 498                         rType = Object.class;
 499                     }
 500                     return MethodHandles.identity(rType);
 501                 }
 502             };
 503 
 504     /**
 505      * Test method's name.
 506      */
 507     public final String name;
 508 
 509     private TestMethods(String name) {
 510         this.name = name;
 511     }
 512 
 513     protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
 514         throw new UnsupportedOperationException("TESTBUG: getMH method is not implemented for test method " + this);
 515     }
 516 
 517     /**
 518      * Creates an adapter method handle depending on a test method from
 519      * MethodHandles class. Adapter is what is returned by the test method. This
 520      * method is able to create two kinds of adapters, their type will be the
 521      * same, but return values are different.
 522      *
 523      * @param data a Map containing data to create a method handle, can be
 524      * obtained by {@link #getTestCaseData} method
 525      * @param kind defines whether adapter ONE or adapter TWO will be
 526      * initialized. Should be equal to TestMethods.Kind.ONE or
 527      * TestMethods.Kind.TWO
 528      * @return Method handle adapter that behaves according to
 529      * TestMethods.Kind.ONE or TestMethods.Kind.TWO
 530      * @throws java.lang.NoSuchMethodException
 531      * @throws java.lang.IllegalAccessException
 532      */
 533     public MethodHandle getTestCaseMH(Map<String, Object> data, TestMethods.Kind kind)
 534             throws NoSuchMethodException, IllegalAccessException {
 535         if (data == null) {
 536             throw new Error(String.format("TESTBUG: Data for test method %s is not prepared",
 537                     this.name));
 538         }
 539         if (!kind.equals(TestMethods.Kind.ONE) && !kind.equals(TestMethods.Kind.TWO)) {
 540             throw new IllegalArgumentException("TESTBUG: Wrong \"kind\" (" + kind
 541                     + ") arg to getTestCaseMH function."
 542                     + " Should be Kind.ONE or Kind.TWO");
 543         }
 544         return getMH(data, kind);
 545     }
 546 
 547     /**
 548      * Returns a data Map needed for {@link #getTestCaseMH} method.
 549      *
 550      * @return data Map needed for {@link #getTestCaseMH} method
 551      */
 552     public Map<String, Object> getTestCaseData() {
 553         throw new UnsupportedOperationException(
 554                 "TESTBUG: getTestCaseData method is not implemented for test method " + this);
 555     }
 556 
 557     /**
 558      * Enumeration used in methodHandleGenerator to define whether a MH returned
 559      * by this method returns "2" in different type representations, "4", or
 560      * throw an Exception.
 561      */
 562     public static enum Kind {
 563 
 564         ONE(2),
 565         TWO(4),
 566         EXCEPT(0);
 567 
 568         private final int value;
 569 
 570         private Object getValue(Class<?> cl) {
 571             return Helper.castToWrapper(value, cl);
 572         }
 573 
 574         private MethodHandle getBasicMH(Class<?> rType) throws NoSuchMethodException, IllegalAccessException {
 575             MethodHandle result = null;
 576             switch (this) {
 577                 case ONE:
 578                 case TWO:
 579                     if (rType.equals(void.class)) {
 580                         result = MethodHandles.lookup().findVirtual(Kind.class, "returnVoid", MethodType.methodType(void.class));
 581                         result = MethodHandles.insertArguments(result, 0, this);
 582                     } else {
 583                         result = MethodHandles.constant(rType, getValue(rType));
 584                     }
 585                     break;
 586                 case EXCEPT:
 587                     result = MethodHandles.throwException(rType, Exception.class);
 588                     result = MethodHandles.insertArguments(result, 0, new Exception());
 589                     break;
 590             }
 591             return result;
 592         }
 593 
 594         private void returnVoid() {
 595         }
 596 
 597         private Kind(int value) {
 598             this.value = value;
 599         }
 600     }
 601 
 602     /**
 603      * Routine used to obtain a randomly generated method type.
 604      *
 605      * @param arity Arity of returned method type.
 606      * @return MethodType generated randomly.
 607      */
 608     private static MethodType randomMethodTypeGenerator(int arity) {
 609         final Class<?>[] CLASSES = {
 610             Object.class,
 611             int.class,
 612             boolean.class,
 613             byte.class,
 614             short.class,
 615             char.class,
 616             long.class,
 617             float.class,
 618             double.class
 619         };
 620         if (arity > Helper.MAX_ARITY) {
 621             throw new IllegalArgumentException(
 622                     String.format("Arity should not exceed %d!", Helper.MAX_ARITY));
 623         }
 624         List<Class<?>> list = Helper.randomClasses(CLASSES, arity);
 625         list = Helper.getParams(list, false, arity);
 626         int i = Helper.RNG.nextInt(CLASSES.length + 1);
 627         Class<?> rtype = i == CLASSES.length ? void.class : CLASSES[i];
 628         return MethodType.methodType(rtype, list);
 629     }
 630 
 631     /**
 632      * Routine used to obtain a method handles of a given type an kind (return
 633      * value).
 634      *
 635      * @param returnType Type of MH return value.
 636      * @param argTypes Types of MH args.
 637      * @param kind Defines whether the obtained MH returns "1" or "2".
 638      * @return Method handle of the given type.
 639      * @throws NoSuchMethodException
 640      * @throws IllegalAccessException
 641      */
 642     private static MethodHandle methodHandleGenerator(Class<?> returnType,
 643             List<Class<?>> argTypes, TestMethods.Kind kind)
 644             throws NoSuchMethodException, IllegalAccessException {
 645         MethodHandle result;
 646         result = kind.getBasicMH(returnType);
 647         return Helper.addTrailingArgs(result, argTypes.size(), argTypes);
 648     }
 649 
 650     /**
 651      * Routine that generates filter method handles to test
 652      * MethodHandles.filterArguments method.
 653      *
 654      * @param inputType Filter's argument type.
 655      * @param returnType Filter's return type.
 656      * @param kind Filter's return value definer.
 657      * @return A filter method handle, that takes one argument.
 658      * @throws NoSuchMethodException
 659      * @throws IllegalAccessException
 660      */
 661     private static MethodHandle filterGenerator(Class<?> inputType, Class<?> returnType,
 662             TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
 663         MethodHandle tmpMH = kind.getBasicMH(returnType);
 664         if (inputType.equals(void.class)) {
 665             return tmpMH;
 666         }
 667         ArrayList<Class<?>> inputTypeList = new ArrayList<>(1);
 668         inputTypeList.add(inputType);
 669         return Helper.addTrailingArgs(tmpMH, 1, inputTypeList);
 670     }
 671 
 672     private static int argSlotsCount(MethodType mt) {
 673         int result = 0;
 674         for (Class cl : mt.parameterArray()) {
 675             if (cl.equals(long.class) || cl.equals(double.class)) {
 676                 result += 2;
 677             } else {
 678                 result++;
 679             }
 680         }
 681         return result;
 682     }
 683 
 684     private static List<Class<?>> reduceArgListToSlotsCount(List<Class<?>> list,
 685             int desiredSlotCount) {
 686         List<Class<?>> result = new ArrayList<>(desiredSlotCount);
 687         int count = 0;
 688         for (Class<?> cl : list) {
 689             if (count >= desiredSlotCount) {
 690                 break;
 691             }
 692             if (cl.equals(long.class) || cl.equals(double.class)) {
 693                 count += 2;
 694             } else {
 695                 count++;
 696             }
 697             result.add(cl);
 698         }
 699         return result;
 700     }
 701 }