1 /*
   2  * Copyright (c) 2011, 2018, 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 package vm.mlvm.meth.share;
  25 
  26 import java.lang.invoke.MethodHandle;
  27 import java.lang.invoke.MethodType;
  28 import java.util.Arrays;
  29 import java.util.Collections;
  30 import java.util.LinkedList;
  31 import java.util.List;
  32 
  33 import nsk.share.test.LazyIntArrayToString;
  34 import nsk.share.test.TestUtils;
  35 import vm.mlvm.meth.share.transform.v2.MHArrayEnvelopeTFPair;
  36 import vm.mlvm.meth.share.transform.v2.MHCall;
  37 import vm.mlvm.meth.share.transform.v2.MHCollectSpreadTF;
  38 import vm.mlvm.meth.share.transform.v2.MHConstantTF;
  39 import vm.mlvm.meth.share.transform.v2.MHDropTF;
  40 import vm.mlvm.meth.share.transform.v2.MHDropTF2;
  41 import vm.mlvm.meth.share.transform.v2.MHFilterRetValTF;
  42 import vm.mlvm.meth.share.transform.v2.MHFilterTF;
  43 import vm.mlvm.meth.share.transform.v2.MHFoldTF;
  44 import vm.mlvm.meth.share.transform.v2.MHIdentityTF;
  45 import vm.mlvm.meth.share.transform.v2.MHInsertTF;
  46 import vm.mlvm.meth.share.transform.v2.MHMacroTF;
  47 import vm.mlvm.meth.share.transform.v2.MHOutboundCallTF;
  48 import vm.mlvm.meth.share.transform.v2.MHOutboundVirtualCallTF;
  49 import vm.mlvm.meth.share.transform.v2.MHPermuteTF;
  50 import vm.mlvm.meth.share.transform.v2.MHSamTF;
  51 import vm.mlvm.meth.share.transform.v2.MHTF;
  52 import vm.mlvm.meth.share.transform.v2.MHTFPair;
  53 import vm.mlvm.meth.share.transform.v2.MHThrowCatchTFPair;
  54 import vm.mlvm.meth.share.transform.v2.MHVarargsCollectSpreadTF;
  55 import vm.mlvm.share.Env;
  56 
  57 public class MHTransformationGen {
  58 
  59     public static final int MAX_CYCLES = 1000;
  60 
  61     private static final int MAX_ARGUMENTS = 10;
  62 
  63     private static final boolean FILTER_OUT_KNOWN_BUGS = false;
  64 
  65     private static final boolean USE_SAM = false; // Disabled in JDK7
  66     private static final boolean USE_THROW_CATCH = false; // Test bugs
  67 
  68     public static class ThrowCatchTestException extends Throwable {
  69         private static final long serialVersionUID = -6749961303738648241L;
  70     }
  71 
  72     @SuppressWarnings("unused")
  73     public static MHMacroTF createSequence(Argument finalRetVal, Object boundObj, MethodHandle finalMH, Argument[] finalArgs) throws Throwable {
  74         Env.traceDebug("Generating sequence.");
  75 
  76         MHMacroTF graph = new MHMacroTF("sequence");
  77 
  78         MHTF outTF;
  79         if ( boundObj != null )
  80             outTF = new MHOutboundVirtualCallTF(finalRetVal, boundObj, finalMH, finalArgs);
  81         else
  82             outTF = new MHOutboundCallTF(finalRetVal, finalMH, finalArgs);
  83 
  84         Env.traceDebug("Outbound call=%s", outTF);
  85         graph.addTransformation(outTF);
  86 
  87         if ( MAX_CYCLES == 0 )
  88             return graph;
  89 
  90         List<MHTFPair> pendingPWTFs = new LinkedList<MHTFPair>();
  91 
  92         for ( int i = nextInt(MAX_CYCLES); i > 0; i-- ) {
  93             MHCall lastCall = graph.computeInboundCall();
  94             Argument[] lastArgs = lastCall.getArgs();
  95             MethodType type = lastCall.getTargetMH().type();
  96             Argument lastRetVal = lastCall.getRetVal();
  97 
  98             int lastArgsSlots = computeVmSlotCount(lastArgs);
  99             if ( boundObj != null )
 100                 lastArgsSlots += TestTypes.getSlotsCount(boundObj.getClass());
 101 
 102             Env.traceDebug("Current last call: %s", lastCall);
 103 
 104             MHTF tf = null;
 105             MHTFPair tfPair = null;
 106 
 107             int nextCase = nextInt(12);
 108 
 109             Env.traceDebug("Adding case #" + nextCase);
 110             try {
 111                 switch ( nextCase ) {
 112                     case 0: { // add next pending TF
 113                         if ( pendingPWTFs.size() > 0 ) {
 114                             MHTFPair pwtf = pendingPWTFs.remove(0);
 115                             tf = pwtf.getInboundTF(lastCall);
 116                             Env.traceDebug("(adding pending inbound transformation)");
 117                         }
 118                     }
 119                     break;
 120 
 121                     case 1: { // Drop arguments
 122                         int pos = nextInt(lastArgs.length);
 123                         int nArgs = nextInt(MAX_ARGUMENTS);
 124                         if ( nArgs == 0 )
 125                             break;
 126 
 127                         Argument[] values = new Argument[nArgs];
 128                         for ( int j = 0; j < nArgs; j++ )
 129                             values[j] = RandomArgumentGen.next();
 130 
 131                         int valuesSlots = computeVmSlotCount(values);
 132 
 133                         int newValuesCount = nArgs;
 134                         while ( valuesSlots + lastArgsSlots > MAX_ARGUMENTS ) {
 135                             valuesSlots -= TestTypes.getSlotsCount(values[newValuesCount - 1].getType());
 136                             --newValuesCount;
 137                         }
 138 
 139                         if ( newValuesCount != nArgs )
 140                             values = Arrays.copyOf(values, newValuesCount);
 141 
 142                         if ( Env.getRNG().nextBoolean() )
 143                             tf = new MHDropTF(lastCall, pos, values);
 144                         else
 145                             tf = new MHDropTF2(lastCall, pos, values);
 146                     }
 147                     break;
 148 
 149                     case 2: { // Insert arguments
 150                         if ( lastArgs.length == 0 )
 151                             break;
 152 
 153                         int pos = nextInt(lastArgs.length);
 154 
 155                         int p;
 156                         for ( p = pos; p < pos + lastArgs.length; p++ ) {
 157                             if ( ! lastArgs[p % lastArgs.length].isPreserved() )
 158                                 break;
 159                         }
 160 
 161                         pos = p % lastArgs.length;
 162                         if ( lastArgs[pos].isPreserved() )
 163                             break;
 164 
 165                         int nArgs = 1 + nextInt(lastArgs.length - pos - 1);
 166 
 167                         for ( p = pos + 1; p < pos + nArgs; p++ ) {
 168                             if ( lastArgs[p].isPreserved() )
 169                                 break;
 170                         }
 171 
 172                         nArgs = p - pos;
 173 
 174                         Argument[] values = Arrays.copyOfRange(lastArgs, pos, pos + nArgs);
 175 
 176                         tf = new MHInsertTF(lastCall, pos, values, false);
 177                     }
 178                     break;
 179 
 180                     case 3: { // Throw + catch
 181                         if ( ! USE_THROW_CATCH )
 182                             break;
 183 
 184                         if ( lastArgsSlots + 1 >= MAX_ARGUMENTS )
 185                             break;
 186 
 187                         Argument testArg = RandomArgumentGen.next();
 188                         Env.traceDebug("testArgument=%s", testArg);
 189 
 190                         Object testValue2;
 191                         boolean eqTest = (Boolean) RandomValueGen.next(Boolean.class);
 192                         if ( eqTest ) {
 193                             testValue2 = testArg.getValue();
 194                         } else {
 195                             testValue2 = RandomValueGen.nextDistinct(testArg.getType(), testArg.getValue());
 196                         }
 197 
 198                         tfPair = new MHThrowCatchTFPair(lastCall, testArg, testValue2, eqTest, new ThrowCatchTestException());
 199                     }
 200                     break;
 201 
 202                     case 4: { // Permute arguments
 203 
 204                         List<Integer> targetArgNumbers = new LinkedList<Integer>();
 205                         for ( int n = 0;  n < lastArgs.length; n++ )
 206                             targetArgNumbers.add(n);
 207                         Collections.shuffle(targetArgNumbers, Env.getRNG());
 208 
 209                         Argument[] sourceArgs = new Argument[lastArgs.length];
 210                         for ( int n = 0; n < lastArgs.length; n++ ) {
 211                             sourceArgs[targetArgNumbers.get(n)] = lastArgs[n];
 212                         }
 213 
 214                         MethodType newMT = MethodType.methodType(type.returnType(), Arguments.typesArray(sourceArgs));
 215 
 216                         // Java has no other means for converting Integer[] to int[]
 217                         int[] permuteArray = new int[targetArgNumbers.size()];
 218                         for ( int n = 0; n < permuteArray.length; n++ )
 219                             permuteArray[n] = targetArgNumbers.get(n);
 220 
 221                         Env.traceDebug("Permute: permuteArray=%s; newMT=%s; oldMT=%s", new LazyIntArrayToString(permuteArray), newMT, lastCall.getTargetMH());
 222 
 223                         tf = new MHPermuteTF(lastCall, newMT, permuteArray);
 224                     }
 225                     break;
 226 
 227                     case 5: { // Fold arguments
 228                         if ( lastArgs.length == 0 )
 229                             break;
 230 
 231                         Argument arg = lastArgs[0];
 232                         if ( arg.isPreserved() )
 233                             break;
 234 
 235                         Argument[] restOfArgs = TestUtils.cdr(lastArgs);
 236 
 237                         MHMacroTF mTF = new MHMacroTF("fold arguments");
 238                         mTF.addOutboundCall(lastCall);
 239 
 240                         MHCall combinerCall = mTF.addTransformation(new MHDropTF(
 241                                 mTF.addTransformation(new MHConstantTF(arg)),
 242                                 0, restOfArgs
 243                         ));
 244 
 245                         Env.traceDebug("combinerCall=%s", combinerCall);
 246                         Env.traceDebug("targetCall=%s", lastCall);
 247 
 248                         mTF.addTransformation(new MHFoldTF(
 249                                 lastCall,
 250                                 combinerCall
 251                         ));
 252 
 253                         tf = mTF;
 254                     }
 255                     break;
 256 
 257                     case 6: { // Filter arguments
 258                         if ( lastArgs.length == 0 )
 259                             break;
 260 
 261                         int pos = nextInt(lastArgs.length);
 262                         int nArgs = 1 + nextInt(lastArgs.length - pos - 1);
 263 
 264                         MHMacroTF mTF = new MHMacroTF("identity filter arguments");
 265                         mTF.addOutboundCall(lastCall);
 266 
 267                         MHCall[] filters = new MHCall[nArgs];
 268                         for ( int n = 0; n < filters.length; n++ ) {
 269                             if ( nextInt(5) != 0 ) {
 270                                 filters[n] = mTF.addTransformation(new MHIdentityTF(lastArgs[n + pos]));
 271                             }
 272                         }
 273 
 274                         mTF.addTransformation(new MHFilterTF(lastCall, pos, filters));
 275 
 276                         tf = mTF;
 277                     }
 278                     break;
 279 
 280                     case 7: { // filter
 281                         if ( lastArgs.length <= 1 )
 282                             break;
 283 
 284                         int pos = nextInt(lastArgs.length);
 285                         int nArgs;
 286                         if ( pos == lastArgs.length - 1 )
 287                             nArgs = 1;
 288                         else
 289                             nArgs = 1 + nextInt(lastArgs.length - pos - 1);
 290 
 291                         MHMacroTF mTF = new MHMacroTF("replace filter arguments");
 292                         mTF.addOutboundCall(lastCall);
 293 
 294                         MHCall[] filters = new MHCall[nArgs];
 295 
 296                         loop:
 297                         for ( int n = 0; n < nArgs; n++ ) {
 298                             Argument arg = lastArgs[pos + n];
 299                             if ( nextInt(5) == 0 || arg.isPreserved() )
 300                                 continue;
 301 
 302                             Class<?> argType = arg.getType();
 303                             Object value = RandomValueGen.next(argType);
 304                             Argument newArg = new Argument(argType, value);
 305 
 306                             filters[n] = mTF.addTransformation(new MHDropTF(
 307                                              mTF.addTransformation(new MHConstantTF(arg)),
 308                                              0, new Argument[] { newArg }
 309                                          ));
 310                         }
 311 
 312                         mTF.addTransformation(new MHFilterTF(lastCall, pos, filters));
 313 
 314                         tf = mTF;
 315                     }
 316                     break;
 317 
 318                     case 8: { // Filter return value
 319                         if ( lastRetVal.isPreserved() )
 320                             break;
 321 
 322                         Class<?> lastRetType = lastRetVal.getType();
 323                         if ( lastRetType.equals(void.class) )
 324                             break;
 325 
 326                         Argument newRetVal = new Argument(lastRetType, RandomValueGen.next(lastRetType));
 327 
 328                         MHMacroTF mTF = new MHMacroTF("filter retval");
 329                         mTF.addOutboundCall(lastCall);
 330                         mTF.addTransformation(new MHFilterRetValTF(lastCall,
 331                                 mTF.addTransformation(new MHDropTF(
 332                                             mTF.addTransformation(new MHConstantTF(newRetVal)),
 333                                             0,
 334                                             new Argument[] { lastRetVal }
 335                                 ))
 336                         ));
 337 
 338                         tf = mTF;
 339                     }
 340                     break;
 341 
 342                     case 9: { // SAM
 343                         if ( ! USE_SAM )
 344                             break;
 345 
 346                         tf = new MHSamTF(lastCall);
 347                     }
 348                     break;
 349 
 350                     case 10: { // Envelope argument into array
 351                         if ( lastArgs.length >= 0 )
 352                             break;
 353 
 354                         int arraySize = 1 + nextInt(0xffff);
 355                         int arrayIdx = nextInt(arraySize);
 356                         int argNum = nextInt(lastArgs.length);
 357                         tfPair = new MHArrayEnvelopeTFPair(lastCall, argNum, arrayIdx, arraySize);
 358                     }
 359                     break;
 360 
 361                     case 11: { // Collect + spread
 362                         if ( nextInt(1) == 0 )
 363                             tf = new MHCollectSpreadTF(lastCall);
 364                         else
 365                             tf = new MHVarargsCollectSpreadTF(lastCall);
 366                     }
 367                     break;
 368                 }
 369 
 370                 if ( FILTER_OUT_KNOWN_BUGS ) {
 371                     if ( tfPair != null ) {
 372                         Env.traceDebug("Checking transformation pair %s", tfPair);
 373 
 374                         tfPair.getInboundTF(tfPair.getOutboundTF().computeInboundCall()).computeInboundCall();
 375                     } else if ( tf != null ) {
 376                         Env.traceDebug("Checking transformation %s", tf);
 377                         tf.computeInboundCall();
 378                     }
 379                 }
 380 
 381             } catch ( Throwable e ) {
 382                 if ( ! FILTER_OUT_KNOWN_BUGS )
 383                     throw e;
 384 
 385                 String msg = e.getMessage();
 386                 for ( Throwable t = e.getCause(); t != null; t = t.getCause() ) {
 387                     msg += " ";
 388                     msg += t.getMessage();
 389                 }
 390 
 391                 if ( msg != null
 392                   && ! msg.contains("NONE SO FAR 2011-07-10")
 393                    ) {
 394                     throw e;
 395                 }
 396 
 397                 Env.traceDebug("Failed to add transformation %s; Error: %s", tf, msg);
 398                 tfPair = null;
 399                 tf = null;
 400             }
 401 
 402             if ( tfPair != null ) {
 403                 MHTF oTF = tfPair.getOutboundTF();
 404                 Env.traceDebug("Adding outbound transformation %s", oTF);
 405                 graph.addTransformation(oTF);
 406                 pendingPWTFs.add(tfPair);
 407             } else if ( tf != null ) {
 408                 Env.traceDebug("Adding transformation %s", tf);
 409                 graph.addTransformation(tf);
 410             } else {
 411                 Env.traceDebug("Skipping transformation");
 412             }
 413         }
 414 
 415         while ( pendingPWTFs.size() > 0 ) {
 416             MHTFPair pwtf = pendingPWTFs.remove(0);
 417             MHTF tf = pwtf.getInboundTF(graph.computeInboundCall());
 418 
 419             Env.traceDebug("Adding pending inbound transformation: %s", tf);
 420             graph.addTransformation(tf);
 421         }
 422 
 423         Env.traceVerbose("MHTransformationGen produced graph: %s", graph);
 424 
 425         return graph;
 426     }
 427 
 428     private static int computeVmSlotCount(Argument[] values) {
 429         int count = 0;
 430         for ( Argument v : values )
 431             count += TestTypes.getSlotsCount(v.getType());
 432         return count;
 433     }
 434 
 435     public static Object callSequence(MHMacroTF seq, boolean checkRetVal) throws Throwable {
 436         Env.traceVerbose("Calling sequence...");
 437         MHCall call = seq.computeInboundCall();
 438         Object result;
 439         try {
 440             if ( checkRetVal ) {
 441                 result = call.callAndCheckRetVal();
 442             } else {
 443                 result = call.call();
 444             }
 445         } catch ( Throwable t ) {
 446             Env.traceNormal(t, "Exception during calling a sequence");
 447             throw (Exception) (new Exception("Exception in sequence " + seq.toString()).initCause(t));
 448         }
 449         Env.traceVerbose("Sequence result = %s", result);
 450         return result;
 451     }
 452 
 453     public static Object createAndCallSequence(Argument finalRetVal, Object boundObj, MethodHandle finalMH, Argument[] finalArgs, boolean checkRetVal) throws Throwable {
 454         return callSequence(createSequence(finalRetVal, boundObj, finalMH, finalArgs), checkRetVal);
 455     }
 456 
 457     public static void transformToMatchArgsNum(MHMacroTF graph, int argNumMin, int argNumMax) throws Throwable {
 458         MHCall lastCall = graph.computeInboundCall();
 459         Argument[] lastArgs = lastCall.getArgs();
 460 
 461         if ( lastArgs.length > argNumMax ) {
 462 
 463             // TODO: preserved args
 464             MHTF tf = new MHInsertTF(lastCall, argNumMax, Arrays.copyOfRange(lastArgs, argNumMax, lastArgs.length), false);
 465             Env.traceVerbose("Adding transformation to match %d limit: %s", argNumMax, tf);
 466             graph.addTransformation(tf);
 467 
 468         } else if ( lastArgs.length < argNumMin ) {
 469 
 470             int argsToInsert = argNumMin - lastArgs.length;
 471 
 472             Argument[] values = new Argument[argsToInsert];
 473             for ( int j = 0; j < argsToInsert; j++ )
 474                 values[j] = RandomArgumentGen.next();
 475 
 476             int pos = 0;
 477 
 478             MHTF tf = new MHDropTF(lastCall, pos, values);
 479             Env.traceVerbose("Adding transformation to match %d arguments: %s", argNumMin, tf);
 480             graph.addTransformation(tf);
 481 
 482         }
 483     }
 484 
 485     private static int nextInt(int i) {
 486         if ( i == 0 )
 487             return 0;
 488         else
 489             return Env.getRNG().nextInt(i);
 490     }
 491 }