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                     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") {
 379                 @Override
 380                 public Map<String, Object> getTestCaseData() {
 381                     Map<String, Object> data = new HashMap<>();
 382                     int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY);
 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") {
 395                 @Override
 396                 public Map<String, Object> getTestCaseData() {
 397                     Map<String, Object> data = new HashMap<>();
 398                     int desiredArity = Helper.RNG.nextInt(Helper.MAX_ARITY);
 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(Helper.MAX_ARITY);
 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(Helper.MAX_ARITY);
 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(Helper.MAX_ARITY);
 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(Helper.MAX_ARITY);
 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 TestMethods(String name) {
 507         this.name = name;
 508     }
 509 
 510     protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
 511         throw new UnsupportedOperationException("TESTBUG: getMH method is not implemented for test method " + this);
 512     }
 513 
 514     /**
 515      * Creates an adapter method handle depending on a test method from
 516      * MethodHandles class. Adapter is what is returned by the test method. This
 517      * method is able to create two kinds of adapters, their type will be the
 518      * same, but return values are different.
 519      *
 520      * @param data a Map containing data to create a method handle, can be
 521      * obtained by {@link #getTestCaseData} method
 522      * @param kind defines whether adapter ONE or adapter TWO will be
 523      * initialized. Should be equal to TestMethods.Kind.ONE or
 524      * TestMethods.Kind.TWO
 525      * @return Method handle adapter that behaves according to
 526      * TestMethods.Kind.ONE or TestMethods.Kind.TWO
 527      * @throws java.lang.NoSuchMethodException
 528      * @throws java.lang.IllegalAccessException
 529      */
 530     public MethodHandle getTestCaseMH(Map<String, Object> data, TestMethods.Kind kind)
 531             throws NoSuchMethodException, IllegalAccessException {
 532         if (data == null) {
 533             throw new Error(String.format("TESTBUG: Data for test method %s is not prepared",
 534                     this.name));
 535         }
 536         if (!kind.equals(TestMethods.Kind.ONE) && !kind.equals(TestMethods.Kind.TWO)) {
 537             throw new IllegalArgumentException("TESTBUG: Wrong \"kind\" (" + kind
 538                     + ") arg to getTestCaseMH function."
 539                     + " Should be Kind.ONE or Kind.TWO");
 540         }
 541         return getMH(data, kind);
 542     }
 543 
 544     /**
 545      * Returns a data Map needed for {@link #getTestCaseMH} method.
 546      *
 547      * @return data Map needed for {@link #getTestCaseMH} method
 548      */
 549     public Map<String, Object> getTestCaseData() {
 550         throw new UnsupportedOperationException(
 551                 "TESTBUG: getTestCaseData method is not implemented for test method " + this);
 552     }
 553 
 554     /**
 555      * Enumeration used in methodHandleGenerator to define whether a MH returned
 556      * by this method returns "2" in different type representations, "4", or
 557      * throw an Exception.
 558      */
 559     public static enum Kind {
 560 
 561         ONE(2),
 562         TWO(4),
 563         EXCEPT(0);
 564 
 565         private final int value;
 566 
 567         private Object getValue(Class<?> cl) {
 568             return Helper.castToWrapper(value, cl);
 569         }
 570 
 571         private MethodHandle getBasicMH(Class<?> rType) throws NoSuchMethodException, IllegalAccessException {
 572             MethodHandle result = null;
 573             switch (this) {
 574                 case ONE:
 575                 case TWO:
 576                     if (rType.equals(void.class)) {
 577                         result = MethodHandles.lookup().findVirtual(Kind.class, "returnVoid", MethodType.methodType(void.class));
 578                         result = MethodHandles.insertArguments(result, 0, this);
 579                     } else {
 580                         result = MethodHandles.constant(rType, getValue(rType));
 581                     }
 582                     break;
 583                 case EXCEPT:
 584                     result = MethodHandles.throwException(rType, Exception.class);
 585                     result = MethodHandles.insertArguments(result, 0, new Exception());
 586                     break;
 587             }
 588             return result;
 589         }
 590 
 591         private void returnVoid() {
 592         }
 593 
 594         private Kind(int value) {
 595             this.value = value;
 596         }
 597     }
 598 
 599     /**
 600      * Routine used to obtain a randomly generated method type.
 601      *
 602      * @param arity Arity of returned method type.
 603      * @return MethodType generated randomly.
 604      */
 605     private static MethodType randomMethodTypeGenerator(int arity) {
 606         final Class<?>[] CLASSES = {
 607             Object.class,
 608             int.class,
 609             boolean.class,
 610             byte.class,
 611             short.class,
 612             char.class,
 613             long.class,
 614             float.class,
 615             double.class
 616         };
 617         if (arity > Helper.MAX_ARITY) {
 618             throw new IllegalArgumentException(
 619                     String.format("Arity should not exceed %d!", Helper.MAX_ARITY));
 620         }
 621         List<Class<?>> list = Helper.randomClasses(CLASSES, arity);
 622         list = Helper.getParams(list, false, arity);
 623         int i = Helper.RNG.nextInt(CLASSES.length + 1);
 624         Class<?> rtype = i == CLASSES.length ? void.class : CLASSES[i];
 625         return MethodType.methodType(rtype, list);
 626     }
 627 
 628     /**
 629      * Routine used to obtain a method handles of a given type an kind (return
 630      * value).
 631      *
 632      * @param returnType Type of MH return value.
 633      * @param argTypes Types of MH args.
 634      * @param kind Defines whether the obtained MH returns "1" or "2".
 635      * @return Method handle of the given type.
 636      * @throws NoSuchMethodException
 637      * @throws IllegalAccessException
 638      */
 639     private static MethodHandle methodHandleGenerator(Class<?> returnType,
 640             List<Class<?>> argTypes, TestMethods.Kind kind)
 641             throws NoSuchMethodException, IllegalAccessException {
 642         MethodHandle result;
 643         result = kind.getBasicMH(returnType);
 644         return Helper.addTrailingArgs(result, argTypes.size(), argTypes);
 645     }
 646 
 647     /**
 648      * Routine that generates filter method handles to test
 649      * MethodHandles.filterArguments method.
 650      *
 651      * @param inputType Filter's argument type.
 652      * @param returnType Filter's return type.
 653      * @param kind Filter's return value definer.
 654      * @return A filter method handle, that takes one argument.
 655      * @throws NoSuchMethodException
 656      * @throws IllegalAccessException
 657      */
 658     private static MethodHandle filterGenerator(Class<?> inputType, Class<?> returnType,
 659             TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
 660         MethodHandle tmpMH = kind.getBasicMH(returnType);
 661         if (inputType.equals(void.class)) {
 662             return tmpMH;
 663         }
 664         ArrayList<Class<?>> inputTypeList = new ArrayList<>(1);
 665         inputTypeList.add(inputType);
 666         return Helper.addTrailingArgs(tmpMH, 1, inputTypeList);
 667     }
 668 
 669     private static int argSlotsCount(MethodType mt) {
 670         int result = 0;
 671         for (Class cl : mt.parameterArray()) {
 672             if (cl.equals(long.class) || cl.equals(double.class)) {
 673                 result += 2;
 674             } else {
 675                 result++;
 676             }
 677         }
 678         return result;
 679     }
 680 
 681     private static List<Class<?>> reduceArgListToSlotsCount(List<Class<?>> list,
 682             int desiredSlotCount) {
 683         List<Class<?>> result = new ArrayList<>(desiredSlotCount);
 684         int count = 0;
 685         for (Class<?> cl : list) {
 686             if (count >= desiredSlotCount) {
 687                 break;
 688             }
 689             if (cl.equals(long.class) || cl.equals(double.class)) {
 690                 count += 2;
 691             } else {
 692                 count++;
 693             }
 694             result.add(cl);
 695         }
 696         return result;
 697     }
 698 }