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