/* * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package vm.mlvm.meth.share; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; import nsk.share.test.LazyIntArrayToString; import nsk.share.test.TestUtils; import vm.mlvm.meth.share.transform.v2.MHArrayEnvelopeTFPair; import vm.mlvm.meth.share.transform.v2.MHCall; import vm.mlvm.meth.share.transform.v2.MHCollectSpreadTF; import vm.mlvm.meth.share.transform.v2.MHConstantTF; import vm.mlvm.meth.share.transform.v2.MHDropTF; import vm.mlvm.meth.share.transform.v2.MHDropTF2; import vm.mlvm.meth.share.transform.v2.MHFilterRetValTF; import vm.mlvm.meth.share.transform.v2.MHFilterTF; import vm.mlvm.meth.share.transform.v2.MHFoldTF; import vm.mlvm.meth.share.transform.v2.MHIdentityTF; import vm.mlvm.meth.share.transform.v2.MHInsertTF; import vm.mlvm.meth.share.transform.v2.MHMacroTF; import vm.mlvm.meth.share.transform.v2.MHOutboundCallTF; import vm.mlvm.meth.share.transform.v2.MHOutboundVirtualCallTF; import vm.mlvm.meth.share.transform.v2.MHPermuteTF; import vm.mlvm.meth.share.transform.v2.MHSamTF; import vm.mlvm.meth.share.transform.v2.MHTF; import vm.mlvm.meth.share.transform.v2.MHTFPair; import vm.mlvm.meth.share.transform.v2.MHThrowCatchTFPair; import vm.mlvm.meth.share.transform.v2.MHVarargsCollectSpreadTF; import vm.mlvm.share.Env; public class MHTransformationGen { public static final int MAX_CYCLES = 1000; private static final int MAX_ARGUMENTS = 10; private static final boolean FILTER_OUT_KNOWN_BUGS = false; private static final boolean USE_SAM = false; // Disabled in JDK7 private static final boolean USE_THROW_CATCH = false; // Test bugs public static class ThrowCatchTestException extends Throwable { private static final long serialVersionUID = -6749961303738648241L; } @SuppressWarnings("unused") public static MHMacroTF createSequence(Argument finalRetVal, Object boundObj, MethodHandle finalMH, Argument[] finalArgs) throws Throwable { Env.traceDebug("Generating sequence."); MHMacroTF graph = new MHMacroTF("sequence"); MHTF outTF; if ( boundObj != null ) outTF = new MHOutboundVirtualCallTF(finalRetVal, boundObj, finalMH, finalArgs); else outTF = new MHOutboundCallTF(finalRetVal, finalMH, finalArgs); Env.traceDebug("Outbound call=%s", outTF); graph.addTransformation(outTF); if ( MAX_CYCLES == 0 ) return graph; List pendingPWTFs = new LinkedList(); for ( int i = nextInt(MAX_CYCLES); i > 0; i-- ) { MHCall lastCall = graph.computeInboundCall(); Argument[] lastArgs = lastCall.getArgs(); MethodType type = lastCall.getTargetMH().type(); Argument lastRetVal = lastCall.getRetVal(); int lastArgsSlots = computeVmSlotCount(lastArgs); if ( boundObj != null ) lastArgsSlots += TestTypes.getSlotsCount(boundObj.getClass()); Env.traceDebug("Current last call: %s", lastCall); MHTF tf = null; MHTFPair tfPair = null; int nextCase = nextInt(12); Env.traceDebug("Adding case #" + nextCase); try { switch ( nextCase ) { case 0: { // add next pending TF if ( pendingPWTFs.size() > 0 ) { MHTFPair pwtf = pendingPWTFs.remove(0); tf = pwtf.getInboundTF(lastCall); Env.traceDebug("(adding pending inbound transformation)"); } } break; case 1: { // Drop arguments int pos = nextInt(lastArgs.length); int nArgs = nextInt(MAX_ARGUMENTS); if ( nArgs == 0 ) break; Argument[] values = new Argument[nArgs]; for ( int j = 0; j < nArgs; j++ ) values[j] = RandomArgumentGen.next(); int valuesSlots = computeVmSlotCount(values); int newValuesCount = nArgs; while ( valuesSlots + lastArgsSlots > MAX_ARGUMENTS ) { valuesSlots -= TestTypes.getSlotsCount(values[newValuesCount - 1].getType()); --newValuesCount; } if ( newValuesCount != nArgs ) values = Arrays.copyOf(values, newValuesCount); if ( Env.getRNG().nextBoolean() ) tf = new MHDropTF(lastCall, pos, values); else tf = new MHDropTF2(lastCall, pos, values); } break; case 2: { // Insert arguments if ( lastArgs.length == 0 ) break; int pos = nextInt(lastArgs.length); int p; for ( p = pos; p < pos + lastArgs.length; p++ ) { if ( ! lastArgs[p % lastArgs.length].isPreserved() ) break; } pos = p % lastArgs.length; if ( lastArgs[pos].isPreserved() ) break; int nArgs = 1 + nextInt(lastArgs.length - pos - 1); for ( p = pos + 1; p < pos + nArgs; p++ ) { if ( lastArgs[p].isPreserved() ) break; } nArgs = p - pos; Argument[] values = Arrays.copyOfRange(lastArgs, pos, pos + nArgs); tf = new MHInsertTF(lastCall, pos, values, false); } break; case 3: { // Throw + catch if ( ! USE_THROW_CATCH ) break; if ( lastArgsSlots + 1 >= MAX_ARGUMENTS ) break; Argument testArg = RandomArgumentGen.next(); Env.traceDebug("testArgument=%s", testArg); Object testValue2; boolean eqTest = (Boolean) RandomValueGen.next(Boolean.class); if ( eqTest ) { testValue2 = testArg.getValue(); } else { testValue2 = RandomValueGen.nextDistinct(testArg.getType(), testArg.getValue()); } tfPair = new MHThrowCatchTFPair(lastCall, testArg, testValue2, eqTest, new ThrowCatchTestException()); } break; case 4: { // Permute arguments List targetArgNumbers = new LinkedList(); for ( int n = 0; n < lastArgs.length; n++ ) targetArgNumbers.add(n); Collections.shuffle(targetArgNumbers, Env.getRNG()); Argument[] sourceArgs = new Argument[lastArgs.length]; for ( int n = 0; n < lastArgs.length; n++ ) { sourceArgs[targetArgNumbers.get(n)] = lastArgs[n]; } MethodType newMT = MethodType.methodType(type.returnType(), Arguments.typesArray(sourceArgs)); // Java has no other means for converting Integer[] to int[] int[] permuteArray = new int[targetArgNumbers.size()]; for ( int n = 0; n < permuteArray.length; n++ ) permuteArray[n] = targetArgNumbers.get(n); Env.traceDebug("Permute: permuteArray=%s; newMT=%s; oldMT=%s", new LazyIntArrayToString(permuteArray), newMT, lastCall.getTargetMH()); tf = new MHPermuteTF(lastCall, newMT, permuteArray); } break; case 5: { // Fold arguments if ( lastArgs.length == 0 ) break; Argument arg = lastArgs[0]; if ( arg.isPreserved() ) break; Argument[] restOfArgs = TestUtils.cdr(lastArgs); MHMacroTF mTF = new MHMacroTF("fold arguments"); mTF.addOutboundCall(lastCall); MHCall combinerCall = mTF.addTransformation(new MHDropTF( mTF.addTransformation(new MHConstantTF(arg)), 0, restOfArgs )); Env.traceDebug("combinerCall=%s", combinerCall); Env.traceDebug("targetCall=%s", lastCall); mTF.addTransformation(new MHFoldTF( lastCall, combinerCall )); tf = mTF; } break; case 6: { // Filter arguments if ( lastArgs.length == 0 ) break; int pos = nextInt(lastArgs.length); int nArgs = 1 + nextInt(lastArgs.length - pos - 1); MHMacroTF mTF = new MHMacroTF("identity filter arguments"); mTF.addOutboundCall(lastCall); MHCall[] filters = new MHCall[nArgs]; for ( int n = 0; n < filters.length; n++ ) { if ( nextInt(5) != 0 ) { filters[n] = mTF.addTransformation(new MHIdentityTF(lastArgs[n + pos])); } } mTF.addTransformation(new MHFilterTF(lastCall, pos, filters)); tf = mTF; } break; case 7: { // filter if ( lastArgs.length <= 1 ) break; int pos = nextInt(lastArgs.length); int nArgs; if ( pos == lastArgs.length - 1 ) nArgs = 1; else nArgs = 1 + nextInt(lastArgs.length - pos - 1); MHMacroTF mTF = new MHMacroTF("replace filter arguments"); mTF.addOutboundCall(lastCall); MHCall[] filters = new MHCall[nArgs]; loop: for ( int n = 0; n < nArgs; n++ ) { Argument arg = lastArgs[pos + n]; if ( nextInt(5) == 0 || arg.isPreserved() ) continue; Class argType = arg.getType(); Object value = RandomValueGen.next(argType); Argument newArg = new Argument(argType, value); filters[n] = mTF.addTransformation(new MHDropTF( mTF.addTransformation(new MHConstantTF(arg)), 0, new Argument[] { newArg } )); } mTF.addTransformation(new MHFilterTF(lastCall, pos, filters)); tf = mTF; } break; case 8: { // Filter return value if ( lastRetVal.isPreserved() ) break; Class lastRetType = lastRetVal.getType(); if ( lastRetType.equals(void.class) ) break; Argument newRetVal = new Argument(lastRetType, RandomValueGen.next(lastRetType)); MHMacroTF mTF = new MHMacroTF("filter retval"); mTF.addOutboundCall(lastCall); mTF.addTransformation(new MHFilterRetValTF(lastCall, mTF.addTransformation(new MHDropTF( mTF.addTransformation(new MHConstantTF(newRetVal)), 0, new Argument[] { lastRetVal } )) )); tf = mTF; } break; case 9: { // SAM if ( ! USE_SAM ) break; tf = new MHSamTF(lastCall); } break; case 10: { // Envelope argument into array if ( lastArgs.length >= 0 ) break; int arraySize = 1 + nextInt(0xffff); int arrayIdx = nextInt(arraySize); int argNum = nextInt(lastArgs.length); tfPair = new MHArrayEnvelopeTFPair(lastCall, argNum, arrayIdx, arraySize); } break; case 11: { // Collect + spread if ( nextInt(1) == 0 ) tf = new MHCollectSpreadTF(lastCall); else tf = new MHVarargsCollectSpreadTF(lastCall); } break; } if ( FILTER_OUT_KNOWN_BUGS ) { if ( tfPair != null ) { Env.traceDebug("Checking transformation pair %s", tfPair); tfPair.getInboundTF(tfPair.getOutboundTF().computeInboundCall()).computeInboundCall(); } else if ( tf != null ) { Env.traceDebug("Checking transformation %s", tf); tf.computeInboundCall(); } } } catch ( Throwable e ) { if ( ! FILTER_OUT_KNOWN_BUGS ) throw e; String msg = e.getMessage(); for ( Throwable t = e.getCause(); t != null; t = t.getCause() ) { msg += " "; msg += t.getMessage(); } if ( msg != null && ! msg.contains("NONE SO FAR 2011-07-10") ) { throw e; } Env.traceDebug("Failed to add transformation %s; Error: %s", tf, msg); tfPair = null; tf = null; } if ( tfPair != null ) { MHTF oTF = tfPair.getOutboundTF(); Env.traceDebug("Adding outbound transformation %s", oTF); graph.addTransformation(oTF); pendingPWTFs.add(tfPair); } else if ( tf != null ) { Env.traceDebug("Adding transformation %s", tf); graph.addTransformation(tf); } else { Env.traceDebug("Skipping transformation"); } } while ( pendingPWTFs.size() > 0 ) { MHTFPair pwtf = pendingPWTFs.remove(0); MHTF tf = pwtf.getInboundTF(graph.computeInboundCall()); Env.traceDebug("Adding pending inbound transformation: %s", tf); graph.addTransformation(tf); } Env.traceVerbose("MHTransformationGen produced graph: %s", graph); return graph; } private static int computeVmSlotCount(Argument[] values) { int count = 0; for ( Argument v : values ) count += TestTypes.getSlotsCount(v.getType()); return count; } public static Object callSequence(MHMacroTF seq, boolean checkRetVal) throws Throwable { Env.traceVerbose("Calling sequence..."); MHCall call = seq.computeInboundCall(); Object result; try { if ( checkRetVal ) { result = call.callAndCheckRetVal(); } else { result = call.call(); } } catch ( Throwable t ) { Env.traceNormal(t, "Exception during calling a sequence"); throw (Exception) (new Exception("Exception in sequence " + seq.toString()).initCause(t)); } Env.traceVerbose("Sequence result = %s", result); return result; } public static Object createAndCallSequence(Argument finalRetVal, Object boundObj, MethodHandle finalMH, Argument[] finalArgs, boolean checkRetVal) throws Throwable { return callSequence(createSequence(finalRetVal, boundObj, finalMH, finalArgs), checkRetVal); } public static void transformToMatchArgsNum(MHMacroTF graph, int argNumMin, int argNumMax) throws Throwable { MHCall lastCall = graph.computeInboundCall(); Argument[] lastArgs = lastCall.getArgs(); if ( lastArgs.length > argNumMax ) { // TODO: preserved args MHTF tf = new MHInsertTF(lastCall, argNumMax, Arrays.copyOfRange(lastArgs, argNumMax, lastArgs.length), false); Env.traceVerbose("Adding transformation to match %d limit: %s", argNumMax, tf); graph.addTransformation(tf); } else if ( lastArgs.length < argNumMin ) { int argsToInsert = argNumMin - lastArgs.length; Argument[] values = new Argument[argsToInsert]; for ( int j = 0; j < argsToInsert; j++ ) values[j] = RandomArgumentGen.next(); int pos = 0; MHTF tf = new MHDropTF(lastCall, pos, values); Env.traceVerbose("Adding transformation to match %d arguments: %s", argNumMin, tf); graph.addTransformation(tf); } } private static int nextInt(int i) { if ( i == 0 ) return 0; else return Env.getRNG().nextInt(i); } }