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 }