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