1 /*
  2  * Copyright (c) 2021, 2022, 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 compiler.lib.ir_framework.test;
 25 
 26 import compiler.lib.ir_framework.*;
 27 import compiler.lib.ir_framework.Compiler;
 28 import compiler.lib.ir_framework.shared.*;
 29 import jdk.test.lib.Platform;
 30 import jdk.test.lib.Utils;
 31 import jdk.test.whitebox.WhiteBox;
 32 
 33 import java.io.PrintWriter;
 34 import java.io.StringWriter;
 35 import java.lang.annotation.Annotation;
 36 import java.lang.reflect.*;
 37 import java.util.*;
 38 import java.util.stream.Collectors;
 39 import java.util.stream.Stream;
 40 
 41 /**
 42  * This class' main method is called from {@link TestFramework} and represents the so-called "test VM". The class is
 43  * the heart of the framework and is responsible for executing all the specified tests in the test class. It uses the
 44  * Whitebox API and reflection to achieve this task.
 45  */
 46 public class TestVM {
 47     private static final WhiteBox WHITE_BOX;
 48 
 49     static {
 50         try {
 51             WHITE_BOX = WhiteBox.getWhiteBox();
 52         } catch (UnsatisfiedLinkError e) {
 53             System.err.println(System.lineSeparator() + """
 54                                ##########################################################
 55                                 - Did you call a test-related interface method from
 56                                   TestFramework in main() of your test? Make sure to
 57                                   only call setup/run methods and no checks or
 58                                   assertions from main() of your test!
 59                                 - Are you rerunning the test VM (TestVM class)
 60                                   directly after a JTreg run? Make sure to start it
 61                                   from within JTwork/scratch and with the flag
 62                                   -DReproduce=true!
 63                                ##########################################################
 64                                """);
 65             throw e;
 66         }
 67     }
 68 
 69     /**
 70      * The default number of warm-up iterations used to warm up a {@link Test} annotated test method.
 71      * Use {@code -DWarmup=XY} to specify a different default value. An individual warm-up can also be
 72      * set by specifying a {@link Warmup} iteration for a test.
 73      */
 74     public static final int WARMUP_ITERATIONS = Integer.parseInt(System.getProperty("Warmup", "2000"));
 75 
 76     private static final boolean TIERED_COMPILATION = (Boolean)WHITE_BOX.getVMFlag("TieredCompilation");
 77     private static final CompLevel TIERED_COMPILATION_STOP_AT_LEVEL;
 78     private static final boolean CLIENT_VM = Platform.isClient();
 79 
 80     static {
 81         CompLevel level = CompLevel.forValue(((Long)WHITE_BOX.getVMFlag("TieredStopAtLevel")).intValue());
 82         if (CLIENT_VM && level == CompLevel.C2) {
 83             // No C2 available, use C1 level without profiling.
 84             level = CompLevel.C1_SIMPLE;
 85         }
 86         TIERED_COMPILATION_STOP_AT_LEVEL = level;
 87     }
 88     public static final boolean TEST_C1 = (TIERED_COMPILATION && TIERED_COMPILATION_STOP_AT_LEVEL.getValue() < CompLevel.C2.getValue()) || CLIENT_VM;
 89 
 90     static final boolean XCOMP = Platform.isComp();
 91     static final boolean VERBOSE = Boolean.getBoolean("Verbose");
 92     private static final boolean PRINT_TIMES = Boolean.getBoolean("PrintTimes");
 93     public static final boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler");
 94     static final boolean EXCLUDE_RANDOM = Boolean.getBoolean("ExcludeRandom");
 95     private static final String TESTLIST = System.getProperty("Test", "");
 96     private static final String EXCLUDELIST = System.getProperty("Exclude", "");
 97     private static final boolean DUMP_REPLAY = Boolean.getBoolean("DumpReplay");
 98     private static final boolean GC_AFTER = Boolean.getBoolean("GCAfter");
 99     private static final boolean SHUFFLE_TESTS = Boolean.parseBoolean(System.getProperty("ShuffleTests", "true"));
100     // Use separate flag as VERIFY_IR could have been set by user but due to other flags it was disabled by flag VM.
101     private static final boolean PRINT_VALID_IR_RULES = Boolean.getBoolean("ShouldDoIRVerification");
102     protected static final long PER_METHOD_TRAP_LIMIT = (Long)WHITE_BOX.getVMFlag("PerMethodTrapLimit");
103     protected static final boolean PROFILE_INTERPRETER = (Boolean)WHITE_BOX.getVMFlag("ProfileInterpreter");
104     protected static final boolean DEOPT_BARRIERS_ALOT = (Boolean)WHITE_BOX.getVMFlag("DeoptimizeNMethodBarriersALot");
105     private static final boolean FLIP_C1_C2 = Boolean.getBoolean("FlipC1C2");
106     private static final boolean IGNORE_COMPILER_CONTROLS = Boolean.getBoolean("IgnoreCompilerControls");
107 
108     private final HashMap<Method, DeclaredTest> declaredTests = new HashMap<>();
109     private final List<AbstractTest> allTests = new ArrayList<>();
110     private final HashMap<String, Method> testMethodMap = new HashMap<>();
111     private final List<String> excludeList;
112     private final List<String> testList;
113     private Set<Class<?>> helperClasses = null; // Helper classes that contain framework annotations to be processed.
114     private final IREncodingPrinter irMatchRulePrinter;
115     private final Class<?> testClass;
116     private final Map<Executable, CompLevel> forceCompileMap = new HashMap<>();
117 
118     private TestVM(Class<?> testClass) {
119         TestRun.check(testClass != null, "Test class cannot be null");
120         this.testClass = testClass;
121         this.testList = createTestFilterList(TESTLIST, testClass);
122         this.excludeList = createTestFilterList(EXCLUDELIST, testClass);
123 
124         if (PRINT_VALID_IR_RULES) {
125             irMatchRulePrinter = new IREncodingPrinter();
126         } else {
127             irMatchRulePrinter = null;
128         }
129     }
130 
131     /**
132      * Parse "test1,test2,test3" into a list.
133      */
134     private static List<String> createTestFilterList(String list, Class<?> testClass) {
135         List<String> filterList = null;
136         if (!list.isEmpty()) {
137             String classPrefix = testClass.getSimpleName() + ".";
138             filterList = new ArrayList<>(Arrays.asList(list.split(",")));
139             for (int i = filterList.size() - 1; i >= 0; i--) {
140                 String test = filterList.get(i);
141                 if (test.indexOf(".") > 0) {
142                     if (test.startsWith(classPrefix)) {
143                         test = test.substring(classPrefix.length());
144                         filterList.set(i, test);
145                     } else {
146                         filterList.remove(i);
147                     }
148                 }
149             }
150         }
151         return filterList;
152     }
153 
154     /**
155      * Main entry point of the test VM.
156      */
157     public static void main(String[] args) {
158         try {
159             String testClassName = args[0];
160             System.out.println("TestVM main() called - about to run tests in class " + testClassName);
161             Class<?> testClass = getClassObject(testClassName, "test");
162 
163             TestVM framework = new TestVM(testClass);
164             framework.addHelperClasses(args);
165             framework.start();
166         } finally {
167             TestFrameworkSocket.closeClientSocket();
168         }
169     }
170 
171     protected static Class<?> getClassObject(String className, String classType) {
172         try {
173             return Class.forName(className);
174         } catch (Exception e) {
175             throw new TestRunException("Could not find " + classType + " class", e);
176         }
177     }
178 
179     /**
180      * Set up all helper classes and verify they are specified correctly.
181      */
182     private void addHelperClasses(String[] args) {
183         Class<?>[] helperClassesList = getHelperClasses(args);
184         if (helperClassesList != null) {
185             TestRun.check(Arrays.stream(helperClassesList).noneMatch(Objects::isNull), "A Helper class cannot be null");
186             this.helperClasses = new HashSet<>();
187 
188             for (Class<?> helperClass : helperClassesList) {
189                 if (Arrays.stream(testClass.getDeclaredClasses()).anyMatch(c -> c == helperClass)) {
190                     // Nested class of test class is automatically treated as helper class
191                     TestFormat.failNoThrow("Nested " + helperClass + " inside test " + testClass + " is implicitly"
192                                            + " treated as helper class and does not need to be specified as such.");
193                     continue;
194                 }
195                 TestRun.check(!this.helperClasses.contains(helperClass), "Cannot add the same class twice: " + helperClass);
196                 this.helperClasses.add(helperClass);
197             }
198         }
199     }
200 
201     private static Class<?>[] getHelperClasses(String[] args) {
202         if (args.length == 1) {
203             return null;
204         }
205         Class<?>[] helperClasses = new Class<?>[args.length - 1]; // First argument is test class
206         for (int i = 1; i < args.length; i++) {
207             String helperClassName = args[i];
208             helperClasses[i - 1] = getClassObject(helperClassName, "helper");
209         }
210         return helperClasses;
211     }
212 
213     private void checkHelperClass(Class<?> clazz) {
214         checkAnnotationsInClass(clazz, "helper");
215         for (Class<?> c : clazz.getDeclaredClasses()) {
216             checkAnnotationsInClass(c, "nested (and helper)");
217         }
218     }
219 
220     private void checkAnnotationsInClass(Class<?> c, String clazzType) {
221         Method[] methods = c.getDeclaredMethods();
222         for (Method m : methods) {
223             TestFormat.checkNoThrow(getAnnotation(m, Test.class) == null,
224                                     "Cannot use @Test annotation in " + clazzType + " " + c + " at " + m);
225             TestFormat.checkNoThrow(getAnnotation(m, Run.class) == null,
226                                     "Cannot use @Run annotation in " + clazzType + " " + c + " at " + m);
227             TestFormat.checkNoThrow(getAnnotation(m, Check.class) == null,
228                                     "Cannot use @Check annotation in " + clazzType + " " + c + " at " + m);
229         }
230     }
231 
232     /**
233      * Only called by internal tests testing the framework itself. Accessed by reflection. Not exposed to normal users.
234      */
235     private static void runTestsOnSameVM(Class<?> testClass) {
236         if (testClass == null) {
237             StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
238             testClass = walker.getCallerClass();
239         }
240         TestVM framework = new TestVM(testClass);
241         framework.start();
242     }
243 
244     /**
245      * Once everything is initialized and set up, start collecting tests and executing them afterwards.
246      */
247     private void start() {
248         setupTests();
249         checkForcedCompilationsCompleted();
250         runTests();
251     }
252 
253     private void setupTests() {
254         // TODO remove this once JDK-8273591 is fixed
255         if (!IGNORE_COMPILER_CONTROLS) {
256             for (Class<?> clazz : testClass.getDeclaredClasses()) {
257                 checkAnnotationsInClass(clazz, "inner");
258             }
259         }
260         if (DUMP_REPLAY) {
261             addReplay();
262         }
263         // Make sure to first setup test methods and make them non-inlineable and only then process compile commands.
264         setupDeclaredTests();
265         processControlAnnotations(testClass);
266         processHelperClasses();
267         setupCheckedAndCustomRunTests();
268 
269         // All remaining tests are simple base tests without check or specific way to run them.
270         addBaseTests();
271         if (PRINT_VALID_IR_RULES) {
272             irMatchRulePrinter.emit();
273             VMInfoPrinter.emit();
274         }
275         TestFormat.throwIfAnyFailures();
276         declaredTests.clear();
277         testMethodMap.clear();
278     }
279 
280     private void addBaseTests() {
281         declaredTests.forEach((m, test) -> {
282             if (test.getAttachedMethod() == null) {
283                 try {
284                     Arguments argumentsAnno = getAnnotation(m, Arguments.class);
285                     TestFormat.check(argumentsAnno != null || m.getParameterCount() == 0, "Missing @Arguments annotation to define arguments of " + m);
286                     BaseTest baseTest = new BaseTest(test, shouldExcludeTest(m.getName()));
287                     allTests.add(baseTest);
288                     if (PRINT_VALID_IR_RULES) {
289                         irMatchRulePrinter.emitRuleEncoding(m, baseTest.isSkipped());
290                     }
291                 } catch (TestFormatException e) {
292                     // Failure logged. Continue and report later.
293                 }
294             }
295         });
296     }
297 
298     /**
299      * Check if user wants to exclude this test by checking the -DTest and -DExclude lists.
300      */
301     private boolean shouldExcludeTest(String testName) {
302         boolean hasTestList = testList != null;
303         boolean hasExcludeList = excludeList != null;
304         if (hasTestList) {
305             return !testList.contains(testName) || (hasExcludeList && excludeList.contains(testName));
306         } else if (hasExcludeList) {
307             return excludeList.contains(testName);
308         }
309         return false;
310     }
311 
312     /**
313      * Generate replay compilation files.
314      */
315     private void addReplay() {
316         String directive = "[{ match: \"*.*\", DumpReplay: true }]";
317         TestFramework.check(WHITE_BOX.addCompilerDirective(directive) == 1, "Failed to add DUMP_REPLAY directive");
318     }
319 
320     private void processControlAnnotations(Class<?> clazz) {
321         if (IGNORE_COMPILER_CONTROLS) {
322             return;
323         }
324         // Also apply compile commands to all inner classes of 'clazz'.
325         ArrayList<Class<?>> classes = new ArrayList<>(Arrays.asList(clazz.getDeclaredClasses()));
326         classes.add(clazz);
327         for (Class<?> c : classes) {
328             applyClassAnnotations(c);
329             List<Executable> executables = new ArrayList<>(Arrays.asList(c.getDeclaredMethods()));
330             Collections.addAll(executables, c.getDeclaredConstructors());
331             for (Executable ex : executables) {
332                 checkClassAnnotations(ex);
333                 try {
334                     applyIndependentCompilationCommands(ex);
335                 } catch (TestFormatException e) {
336                     // Failure logged. Continue and report later.
337                 }
338             }
339 
340             // Only force compilation now because above annotations affect inlining
341             for (Executable ex : executables) {
342                 try {
343                     applyForceCompileCommand(ex);
344                 } catch (TestFormatException e) {
345                     // Failure logged. Continue and report later.
346                 }
347             }
348         }
349     }
350 
351     private void applyClassAnnotations(Class<?> c) {
352         ForceCompileClassInitializer anno = getAnnotation(c, ForceCompileClassInitializer.class);
353         if (anno == null) {
354             return;
355         }
356 
357         // Compile class initializer
358         CompLevel level = anno.value();
359         if (level == CompLevel.SKIP || level == CompLevel.WAIT_FOR_COMPILATION) {
360             TestFormat.failNoThrow("Cannot define compilation level SKIP or WAIT_FOR_COMPILATION in " +
361                                    "@ForceCompileClassInitializer at " + c);
362             return;
363         }
364         level = restrictCompLevel(anno.value());
365         if (level != CompLevel.SKIP) {
366             // Make sure class is initialized to avoid compilation bailout of <clinit>
367             getClassObject(c.getName(), "nested"); // calls Class.forName() to initialize 'c'
368             TestFormat.checkNoThrow(WHITE_BOX.enqueueInitializerForCompilation(c, level.getValue()),
369                                     "Failed to enqueue <clinit> of " + c + " for compilation. Did you specify "
370                                     + "@ForceCompileClassInitializer without providing a static class initialization? "
371                                     + "Make sure to provide any form of static initialization or remove the annotation. "
372                                     + "For debugging purposes, -DIgnoreCompilerControls=true can be used to temporarly "
373                                     + "ignore @ForceCompileClassInitializer annotations.");
374         }
375     }
376 
377     private void checkClassAnnotations(Executable ex) {
378         TestFormat.checkNoThrow(getAnnotation(ex, ForceCompileClassInitializer.class) == null,
379                                 "@ForceCompileClassInitializer only allowed at classes but not at method " + ex);
380     }
381 
382     /**
383      * Exclude a method from compilation with a compiler randomly. Return the compiler for which the method was made
384      * not compilable.
385      */
386     public static Compiler excludeRandomly(Executable ex) {
387         Compiler compiler = switch (Utils.getRandomInstance().nextInt() % 3) {
388             case 1 -> Compiler.C1;
389             case 2 -> Compiler.C2;
390             default -> Compiler.ANY;
391         };
392         WHITE_BOX.makeMethodNotCompilable(ex, compiler.getValue(), false);
393         WHITE_BOX.makeMethodNotCompilable(ex, compiler.getValue(), true);
394         System.out.println("Excluding from " + compiler.name() + " compilation: " + ex);
395         return compiler;
396     }
397 
398     private void applyIndependentCompilationCommands(Executable ex) {
399         ForceInline forceInlineAnno = getAnnotation(ex, ForceInline.class);
400         DontInline dontInlineAnno = getAnnotation(ex, DontInline.class);
401         ForceCompile forceCompileAnno = getAnnotation(ex, ForceCompile.class);
402         DontCompile dontCompileAnno = getAnnotation(ex, DontCompile.class);
403         checkCompilationCommandAnnotations(ex, forceInlineAnno, dontInlineAnno, forceCompileAnno, dontCompileAnno);
404         // First handle inline annotations
405         if (dontInlineAnno != null) {
406             WHITE_BOX.testSetDontInlineMethod(ex, true);
407         } else if (forceInlineAnno != null) {
408             WHITE_BOX.testSetForceInlineMethod(ex, true);
409         }
410         if (dontCompileAnno != null) {
411             dontCompileWithCompiler(ex, dontCompileAnno.value());
412         }
413         if (EXCLUDE_RANDOM && getAnnotation(ex, Test.class) == null && forceCompileAnno == null && dontCompileAnno == null) {
414             // Randomly exclude helper methods from compilation
415             if (Utils.getRandomInstance().nextBoolean()) {
416                 excludeRandomly(ex);
417             }
418         }
419     }
420 
421     private void checkCompilationCommandAnnotations(Executable ex, ForceInline forceInlineAnno, DontInline dontInlineAnno, ForceCompile forceCompileAnno, DontCompile dontCompileAnno) {
422         Test testAnno = getAnnotation(ex, Test.class);
423         Run runAnno = getAnnotation(ex, Run.class);
424         Check checkAnno = getAnnotation(ex, Check.class);
425         TestFormat.check((testAnno == null && runAnno == null && checkAnno == null) || Stream.of(forceCompileAnno, dontCompileAnno, forceInlineAnno, dontInlineAnno).noneMatch(Objects::nonNull),
426                          "Cannot use explicit compile command annotations (@ForceInline, @DontInline, " +
427                          "@ForceCompile or @DontCompile) together with @Test, @Check or @Run: " + ex + ". Use compLevel in @Test for fine tuning.");
428         if (Stream.of(forceInlineAnno, dontCompileAnno, dontInlineAnno).filter(Objects::nonNull).count() > 1) {
429             // Failure
430             TestFormat.check(dontCompileAnno == null || dontInlineAnno == null,
431                              "@DontInline is implicitely done with @DontCompile annotation at " + ex);
432             TestFormat.fail("Cannot mix @ForceInline, @DontInline and @DontCompile at the same time at " + ex);
433         }
434         TestFormat.check(forceInlineAnno == null || dontInlineAnno == null, "Cannot have @ForceInline and @DontInline at the same time at " + ex);
435         if (forceCompileAnno != null && dontCompileAnno != null) {
436             CompLevel forceCompileLevel = forceCompileAnno.value();
437             Compiler dontCompileCompiler = dontCompileAnno.value();
438             TestFormat.check(dontCompileCompiler != Compiler.ANY,
439                              "Cannot have @DontCompile(Compiler.ANY) and @ForceCompile at the same time at " + ex);
440             TestFormat.check(forceCompileLevel != CompLevel.ANY,
441                              "Cannot have @ForceCompile(CompLevel.ANY) and @DontCompile at the same time at " + ex);
442             TestFormat.check(forceCompileLevel.isNotCompilationLevelOfCompiler(dontCompileCompiler),
443                              "Overlapping compilation level and compiler with @ForceCompile and @DontCompile at " + ex);
444         }
445     }
446 
447     /**
448      * Exlude the method from compilation and make sure it is not inlined.
449      */
450     private void dontCompileAndDontInlineMethod(Method m) {
451         if (!IGNORE_COMPILER_CONTROLS) {
452             WHITE_BOX.makeMethodNotCompilable(m, CompLevel.ANY.getValue(), true);
453             WHITE_BOX.makeMethodNotCompilable(m, CompLevel.ANY.getValue(), false);
454             WHITE_BOX.testSetDontInlineMethod(m, true);
455         }
456     }
457 
458     private void dontCompileWithCompiler(Executable ex, Compiler compiler) {
459         if (VERBOSE) {
460             System.out.println("dontCompileWithCompiler " + ex + " , compiler = " + compiler.name());
461         }
462         WHITE_BOX.makeMethodNotCompilable(ex, compiler.getValue(), true);
463         WHITE_BOX.makeMethodNotCompilable(ex, compiler.getValue(), false);
464         if (compiler == Compiler.ANY) {
465             WHITE_BOX.testSetDontInlineMethod(ex, true);
466         }
467     }
468 
469     private void applyForceCompileCommand(Executable ex) {
470         ForceCompile forceCompileAnno = getAnnotation(ex, ForceCompile.class);
471         if (forceCompileAnno != null) {
472             CompLevel compLevel = forceCompileAnno.value();
473             TestFormat.check(compLevel != CompLevel.SKIP && compLevel != CompLevel.WAIT_FOR_COMPILATION,
474                              "Cannot define compilation level SKIP or WAIT_FOR_COMPILATION in @ForceCompile at " + ex);
475             compLevel = restrictCompLevel(forceCompileAnno.value());
476             if (FLIP_C1_C2) {
477                 compLevel = compLevel.flipCompLevel();
478                 compLevel = restrictCompLevel(compLevel.flipCompLevel());
479             }
480             if (EXCLUDE_RANDOM) {
481                 compLevel = compLevel.excludeCompilationRandomly(ex);
482             }
483             if (compLevel != CompLevel.SKIP) {
484                 enqueueForCompilation(ex, compLevel);
485                 forceCompileMap.put(ex, compLevel);
486             }
487         }
488     }
489 
490     static void enqueueForCompilation(Executable ex, CompLevel requestedCompLevel) {
491         if (TestVM.VERBOSE) {
492             System.out.println("enqueueForCompilation " + ex + ", level = " + requestedCompLevel);
493         }
494         CompLevel compLevel = restrictCompLevel(requestedCompLevel);
495         if (compLevel != CompLevel.SKIP) {
496             WHITE_BOX.enqueueMethodForCompilation(ex, compLevel.getValue());
497         } else {
498             System.out.println("Skipped compilation on level " + requestedCompLevel + " due to VM flags not allowing it.");
499         }
500     }
501 
502     /**
503      * Setup @Test annotated method an add them to the declaredTests map to have a convenient way of accessing them
504      * once setting up a framework test (base  checked, or custom run test).
505      */
506     private void setupDeclaredTests() {
507         for (Method m : testClass.getDeclaredMethods()) {
508             Test testAnno = getAnnotation(m, Test.class);
509             try {
510                 if (testAnno != null) {
511                     addDeclaredTest(m);
512                 } else {
513                     TestFormat.checkNoThrow(!m.isAnnotationPresent(IR.class) && !m.isAnnotationPresent(IRs.class),
514                                             "Found @IR annotation on non-@Test method " + m);
515                     TestFormat.checkNoThrow(!m.isAnnotationPresent(Warmup.class) || getAnnotation(m, Run.class) != null,
516                                             "Found @Warmup annotation on non-@Test or non-@Run method " + m);
517                 }
518             } catch (TestFormatException e) {
519                 // Failure logged. Continue and report later.
520             }
521         }
522         TestFormat.checkNoThrow(!declaredTests.isEmpty(), "Did not specify any @Test methods in " + testClass);
523     }
524 
525     private void addDeclaredTest(Method m) {
526         Test testAnno = getAnnotation(m, Test.class);
527         checkTestAnnotations(m, testAnno);
528         Warmup warmup = getAnnotation(m, Warmup.class);
529         int warmupIterations = WARMUP_ITERATIONS;
530         if (warmup != null) {
531             warmupIterations = warmup.value();
532             TestFormat.checkNoThrow(warmupIterations >= 0, "Cannot have negative value for @Warmup at " + m);
533         }
534 
535         if (!IGNORE_COMPILER_CONTROLS) {
536             // Don't inline test methods by default. Do not apply this when -DIgnoreCompilerControls=true is set.
537             WHITE_BOX.testSetDontInlineMethod(m, true);
538         }
539         CompLevel compLevel = restrictCompLevel(testAnno.compLevel());
540         if (FLIP_C1_C2) {
541             compLevel = compLevel.flipCompLevel();
542             compLevel = restrictCompLevel(compLevel.flipCompLevel());
543         }
544         if (EXCLUDE_RANDOM) {
545             compLevel = compLevel.excludeCompilationRandomly(m);
546         }
547         DeclaredTest test = new DeclaredTest(m, ArgumentValue.getArguments(m), compLevel, warmupIterations);
548         declaredTests.put(m, test);
549         testMethodMap.put(m.getName(), m);
550     }
551 
552     private void checkTestAnnotations(Method m, Test testAnno) {
553         TestFormat.check(!testMethodMap.containsKey(m.getName()),
554                          "Cannot overload two @Test methods: " + m + ", " + testMethodMap.get(m.getName()));
555         TestFormat.check(testAnno != null, m + " must be a method with a @Test annotation");
556 
557         Check checkAnno = getAnnotation(m, Check.class);
558         Run runAnno = getAnnotation(m, Run.class);
559         TestFormat.check(checkAnno == null && runAnno == null,
560                          m + " has invalid @Check or @Run annotation while @Test annotation is present.");
561 
562         TestFormat.checkNoThrow(Arrays.stream(m.getParameterTypes()).noneMatch(AbstractInfo.class::isAssignableFrom),
563                                 "Cannot " + AbstractInfo.class + " or any of its subclasses as parameter type at " +
564                                 "@Test method " + m);
565 
566         TestFormat.checkNoThrow(!AbstractInfo.class.isAssignableFrom(m.getReturnType()),
567                                 "Cannot " + AbstractInfo.class + " or any of its subclasses as return type at " +
568                                 "@Test method " + m);
569     }
570 
571 
572     /**
573      * Get the appropriate level as permitted by the test scenario and VM flags.
574      */
575     private static CompLevel restrictCompLevel(CompLevel compLevel) {
576         if (!USE_COMPILER) {
577             return CompLevel.SKIP;
578         }
579         if (compLevel == CompLevel.ANY) {
580             // Use highest available compilation level by default (usually C2).
581             compLevel = TIERED_COMPILATION_STOP_AT_LEVEL;
582         }
583         if (TEST_C1 && compLevel == CompLevel.C2) {
584             return CompLevel.SKIP;
585         }
586         if ((!TIERED_COMPILATION && !CLIENT_VM) && compLevel.getValue() < CompLevel.C2.getValue()) {
587             return CompLevel.SKIP;
588         }
589         if ((TIERED_COMPILATION || CLIENT_VM) && compLevel.getValue() > TIERED_COMPILATION_STOP_AT_LEVEL.getValue()) {
590             return CompLevel.SKIP;
591         }
592         return compLevel;
593     }
594 
595     /**
596      * Verify that the helper classes do not contain illegal framework annotations and then apply the actions as
597      * specified by the different helper class annotations.
598      */
599     private void processHelperClasses() {
600         if (helperClasses != null) {
601             for (Class<?> helperClass : helperClasses) {
602                 // Process the helper classes and apply the explicit compile commands
603                 TestFormat.checkNoThrow(helperClass != testClass,
604                                         "Cannot specify test " + testClass + " as helper class, too.");
605                 checkHelperClass(helperClass);
606                 processControlAnnotations(helperClass);
607             }
608         }
609     }
610 
611     /**
612      * First set up checked (with @Check) and custom run tests (with @Run). All remaining unmatched/unused @Test methods
613      * are treated as base tests and set up as such later.
614      */
615     private void setupCheckedAndCustomRunTests() {
616         for (Method m : testClass.getDeclaredMethods()) {
617             Check checkAnno = getAnnotation(m, Check.class);
618             Run runAnno = getAnnotation(m, Run.class);
619             Arguments argumentsAnno = getAnnotation(m, Arguments.class);
620             try {
621                 TestFormat.check(argumentsAnno == null || (checkAnno == null && runAnno == null),
622                                  "Cannot have @Argument annotation in combination with @Run or @Check at " + m);
623                 if (checkAnno != null) {
624                     addCheckedTest(m, checkAnno, runAnno);
625                 } else if (runAnno != null) {
626                     addCustomRunTest(m, runAnno);
627                 }
628             } catch (TestFormatException e) {
629                 // Failure logged. Continue and report later.
630             }
631         }
632     }
633 
634     /**
635      * Set up a checked test by first verifying the correct format of the @Test and @Check method and then adding it
636      * to the allTests list which keeps track of all framework tests that are eventually executed.
637      */
638     private void addCheckedTest(Method m, Check checkAnno, Run runAnno) {
639         Method testMethod = testMethodMap.get(checkAnno.test());
640         DeclaredTest test = declaredTests.get(testMethod);
641         checkCheckedTest(m, checkAnno, runAnno, testMethod, test);
642         test.setAttachedMethod(m);
643         TestFormat.check(getAnnotation(testMethod, Arguments.class) != null || testMethod.getParameterCount() == 0,
644                          "Missing @Arguments annotation to define arguments of " + testMethod + " required by "
645                          + "checked test " + m);
646         CheckedTest.Parameter parameter = getCheckedTestParameter(m, testMethod);
647         dontCompileAndDontInlineMethod(m);
648         CheckedTest checkedTest = new CheckedTest(test, m, checkAnno, parameter, shouldExcludeTest(testMethod.getName()));
649         allTests.add(checkedTest);
650         if (PRINT_VALID_IR_RULES) {
651             // Only need to emit IR verification information if IR verification is actually performed.
652             irMatchRulePrinter.emitRuleEncoding(testMethod, checkedTest.isSkipped());
653         }
654     }
655 
656     private void checkCheckedTest(Method m, Check checkAnno, Run runAnno, Method testMethod, DeclaredTest test) {
657         TestFormat.check(runAnno == null, m + " has invalid @Run annotation while @Check annotation is present.");
658         TestFormat.check(testMethod != null, "Did not find associated test method \"" + m.getDeclaringClass().getName()
659                                              + "." + checkAnno.test() + "\" for @Check at " + m);
660         TestFormat.check(test != null, "Missing @Test annotation for associated test method " + testMethod + " for @Check at " + m);
661         Method attachedMethod = test.getAttachedMethod();
662         TestFormat.check(attachedMethod == null,
663                          "Cannot use @Test " + testMethod + " for more than one @Run or one @Check method. Found: " + m + ", " + attachedMethod);
664     }
665 
666     /**
667      * Only allow parameters as specified in {@link Check}.
668      */
669     private CheckedTest.Parameter getCheckedTestParameter(Method m, Method testMethod) {
670         boolean firstParameterTestInfo = m.getParameterCount() > 0 && m.getParameterTypes()[0].equals(TestInfo.class);
671         boolean secondParameterTestInfo = m.getParameterCount() > 1 && m.getParameterTypes()[1].equals(TestInfo.class);
672         CheckedTest.Parameter parameter = null;
673         Class<?> testReturnType = testMethod.getReturnType();
674         switch (m.getParameterCount()) {
675             case 0 -> parameter = CheckedTest.Parameter.NONE;
676             case 1 -> {
677                 TestFormat.checkNoThrow(firstParameterTestInfo || m.getParameterTypes()[0] == testReturnType,
678                                         "Single-parameter version of @Check method " + m + " must match return type of @Test " + testMethod);
679                 parameter = firstParameterTestInfo ? CheckedTest.Parameter.TEST_INFO_ONLY : CheckedTest.Parameter.RETURN_ONLY;
680             }
681             case 2 -> {
682                 TestFormat.checkNoThrow(m.getParameterTypes()[0] == testReturnType && secondParameterTestInfo,
683                                         "Two-parameter version of @Check method " + m + " must provide as first parameter the same"
684                                         + " return type as @Test method " + testMethod + " and as second parameter an object of " + TestInfo.class);
685                 parameter = CheckedTest.Parameter.BOTH;
686             }
687             default -> TestFormat.failNoThrow("@Check method " + m + " must provide either a none, single or two-parameter variant.");
688         }
689         return parameter;
690     }
691 
692     /**
693      * Set up a custom run test by first verifying the correct format of the @Test and @Run method and then adding it
694      * to the allTests list which keeps track of all framework tests that are eventually executed.
695      */
696     private void addCustomRunTest(Method m, Run runAnno) {
697         checkRunMethod(m, runAnno);
698         List<DeclaredTest> tests = new ArrayList<>();
699         boolean shouldExcludeTest = true;
700         for (String testName : runAnno.test()) {
701             try {
702                 Method testMethod = testMethodMap.get(testName);
703                 DeclaredTest test = declaredTests.get(testMethod);
704                 checkCustomRunTest(m, testName, testMethod, test, runAnno.mode());
705                 test.setAttachedMethod(m);
706                 tests.add(test);
707                 // Only exclude custom run test if all test methods excluded
708                 shouldExcludeTest &= shouldExcludeTest(testMethod.getName());
709             } catch (TestFormatException e) {
710                 // Logged, continue.
711             }
712         }
713         if (tests.isEmpty()) {
714             return; // There was a format violation. Return.
715         }
716         dontCompileAndDontInlineMethod(m);
717         CustomRunTest customRunTest = new CustomRunTest(m, getAnnotation(m, Warmup.class), runAnno, tests, shouldExcludeTest);
718         allTests.add(customRunTest);
719         if (PRINT_VALID_IR_RULES) {
720             tests.forEach(test -> irMatchRulePrinter.emitRuleEncoding(test.getTestMethod(), customRunTest.isSkipped()));
721         }
722     }
723 
724     /**
725      * Only allow parameters as specified in {@link Run}.
726      */
727     private void checkCustomRunTest(Method m, String testName, Method testMethod, DeclaredTest test, RunMode runMode) {
728         TestFormat.check(testMethod != null, "Did not find associated @Test method \""  + m.getDeclaringClass().getName()
729                                              + "." + testName + "\" specified in @Run at " + m);
730         TestFormat.check(test != null,
731                          "Missing @Test annotation for associated test method " + testName + " for @Run at " + m);
732         Method attachedMethod = test.getAttachedMethod();
733         TestFormat.check(attachedMethod == null,
734                          "Cannot use @Test " + testMethod + " for more than one @Run/@Check method. Found: "
735                          + m + ", " + attachedMethod);
736         TestFormat.check(!test.hasArguments(),
737                          "Cannot use @Arguments at test method " + testMethod + " in combination with @Run method " + m);
738         Warmup warmupAnno = getAnnotation(testMethod, Warmup.class);
739         TestFormat.checkNoThrow(warmupAnno == null,
740                                 "Cannot set @Warmup at @Test method " + testMethod + " when used with its @Run method "
741                                 + m + ". Use @Warmup at @Run method instead.");
742         Test testAnno = getAnnotation(testMethod, Test.class);
743         TestFormat.checkNoThrow(runMode != RunMode.STANDALONE || testAnno.compLevel() == CompLevel.ANY,
744                                 "Setting explicit compilation level for @Test method " + testMethod + " has no effect "
745                                 + "when used with STANDALONE @Run method " + m);
746     }
747 
748     private void checkRunMethod(Method m, Run runAnno) {
749         TestFormat.check(runAnno.test().length > 0, "@Run method " + m + " must specify at least one test method");
750         TestFormat.checkNoThrow(m.getParameterCount() == 0 || (m.getParameterCount() == 1 && m.getParameterTypes()[0].equals(RunInfo.class)),
751                                 "@Run method " + m + " must specify either no parameter or exactly one " + RunInfo.class + " parameter.");
752         Warmup warmupAnno = getAnnotation(m, Warmup.class);
753         TestFormat.checkNoThrow(warmupAnno == null || runAnno.mode() != RunMode.STANDALONE,
754                                 "Cannot set @Warmup at @Run method " + m + " when used with RunMode.STANDALONE. The @Run method is only invoked once.");
755     }
756 
757     private static <T extends Annotation> T getAnnotation(AnnotatedElement element, Class<T> c) {
758         T[] annos =  element.getAnnotationsByType(c);
759         TestFormat.check(annos.length < 2, element + " has duplicated annotations");
760         return Arrays.stream(annos).findFirst().orElse(null);
761     }
762 
763     /**
764      * Ensure that all compilations that were enforced (added to compilation queue) by framework annotations are
765      * completed. Wait if necessary for a short amount of time for their completion.
766      */
767     private void checkForcedCompilationsCompleted() {
768         if (forceCompileMap.isEmpty()) {
769             return;
770         }
771         final long started = System.currentTimeMillis();
772         long elapsed;
773         do {
774             forceCompileMap.entrySet().removeIf(entry -> WHITE_BOX.getMethodCompilationLevel(entry.getKey()) == entry.getValue().getValue());
775             if (forceCompileMap.isEmpty()) {
776                 // All @ForceCompile methods are compiled at the requested level.
777                 return;
778             }
779             // Retry again if not yet compiled.
780             forceCompileMap.forEach(TestVM::enqueueForCompilation);
781             elapsed = System.currentTimeMillis() - started;
782         } while (elapsed < 5000);
783         StringBuilder builder = new StringBuilder();
784         forceCompileMap.forEach((key, value) -> builder.append("- ").append(key).append(" at CompLevel.").append(value)
785                                                        .append(System.lineSeparator()));
786         throw new TestRunException("Could not force compile the following @ForceCompile methods:"
787                                    + System.lineSeparator() + builder.toString());
788     }
789 
790     /**
791      * Once all framework tests are collected, they are run in this method.
792      */
793     private void runTests() {
794         TreeMap<Long, String> durations = (PRINT_TIMES || VERBOSE) ? new TreeMap<>() : null;
795         long startTime = System.nanoTime();
796         List<AbstractTest> testList;
797         boolean testFilterPresent = testFilterPresent();
798         if (testFilterPresent) {
799             // Only run the specified tests by the user filters -DTest and/or -DExclude.
800             testList = allTests.stream().filter(test -> !test.isSkipped()).collect(Collectors.toList());
801             if (testList.isEmpty()) {
802                 // Throw an exception to inform the user about an empty specified test set with -DTest and/or -DExclude
803                 throw new NoTestsRunException();
804             }
805         } else {
806             testList = allTests;
807         }
808 
809         if (SHUFFLE_TESTS) {
810             // Execute tests in random order (execution sequence affects profiling). This is done by default.
811             Collections.shuffle(testList, Utils.getRandomInstance());
812         }
813         StringBuilder builder = new StringBuilder();
814         int failures = 0;
815 
816         // Execute all tests and keep track of each exception that is thrown. These are then reported once all tests
817         // are executing. This prevents a premature exit without running all tests.
818         for (AbstractTest test : testList) {
819             if (VERBOSE) {
820                 System.out.println("Run " + test.toString());
821             }
822             if (testFilterPresent) {
823                 TestFrameworkSocket.write("Run " + test.toString(), TestFrameworkSocket.TESTLIST_TAG, true);
824             }
825             try {
826                 test.run();
827             } catch (TestRunException e) {
828                 StringWriter sw = new StringWriter();
829                 PrintWriter pw = new PrintWriter(sw);
830                 e.printStackTrace(pw);
831                 builder.append(test.toString()).append(":").append(System.lineSeparator()).append(sw.toString())
832                        .append(System.lineSeparator()).append(System.lineSeparator());
833                 failures++;
834             }
835             if (PRINT_TIMES || VERBOSE) {
836                 long endTime = System.nanoTime();
837                 long duration = (endTime - startTime);
838                 durations.put(duration, test.getName());
839                 if (VERBOSE) {
840                     System.out.println("Done " + test.getName() + ": " + duration + " ns = " + (duration / 1000000) + " ms");
841                 }
842             }
843             if (GC_AFTER) {
844                 System.out.println("doing GC");
845                 WHITE_BOX.fullGC();
846             }
847         }
848 
849         // Print execution times
850         if (VERBOSE || PRINT_TIMES) {
851             System.out.println(System.lineSeparator() + System.lineSeparator() + "Test execution times:");
852             for (Map.Entry<Long, String> entry : durations.entrySet()) {
853                 System.out.format("%-10s%15d ns%n", entry.getValue() + ":", entry.getKey());
854             }
855         }
856 
857         if (failures > 0) {
858             // Finally, report all occurred exceptions in a nice format.
859             String msg = System.lineSeparator() + System.lineSeparator() + "Test Failures (" + failures + ")"
860                          + System.lineSeparator() + "----------------" + "-".repeat(String.valueOf(failures).length());
861             throw new TestRunException(msg + System.lineSeparator() + builder.toString());
862         }
863     }
864 
865     private boolean testFilterPresent() {
866         return testList != null || excludeList != null;
867     }
868 
869     enum TriState {
870         Maybe,
871         Yes,
872         No
873     }
874 
875     public static void compile(Method m, CompLevel compLevel) {
876         TestRun.check(compLevel != CompLevel.SKIP && compLevel != CompLevel.WAIT_FOR_COMPILATION,
877                       "Invalid compilation request with level " + compLevel);
878         enqueueForCompilation(m, compLevel);
879     }
880 
881     public static void deoptimize(Method m) {
882         WHITE_BOX.deoptimizeMethod(m);
883     }
884 
885     public static boolean isCompiled(Method m) {
886         return compiledAtLevel(m, CompLevel.ANY) == TriState.Yes;
887     }
888 
889     public static boolean isC1Compiled(Method m) {
890         return compiledByC1(m) == TriState.Yes;
891     }
892 
893     public static boolean isC2Compiled(Method m) {
894         return compiledByC2(m) == TriState.Yes;
895     }
896 
897     public static boolean isCompiledAtLevel(Method m, CompLevel compLevel) {
898         return compiledAtLevel(m, compLevel) == TriState.Yes;
899     }
900 
901     public static void assertDeoptimizedByC1(Method m) {
902         if (isStableDeopt(m, CompLevel.C1_SIMPLE)) {
903             TestRun.check(compiledByC1(m) != TriState.Yes, m + " should have been deoptimized by C1");
904         }
905     }
906 
907     public static void assertDeoptimizedByC2(Method m) {
908         if (isStableDeopt(m, CompLevel.C2)) {
909             TestRun.check(compiledByC2(m) != TriState.Yes, m + " should have been deoptimized by C2");
910         }
911     }
912 
913     /**
914      * Some VM flags could make the deopt assertions unstable.
915      */
916     public static boolean isStableDeopt(Method m, CompLevel level) {
917         return (USE_COMPILER && !XCOMP && !IGNORE_COMPILER_CONTROLS && !TEST_C1 &&
918                 PER_METHOD_TRAP_LIMIT != 0 && PROFILE_INTERPRETER && !DEOPT_BARRIERS_ALOT &&
919                 (!EXCLUDE_RANDOM || WHITE_BOX.isMethodCompilable(m, level.getValue(), false)));
920     }
921 
922     public static void assertCompiledByC1(Method m) {
923         TestRun.check(compiledByC1(m) != TriState.No, m + " should have been C1 compiled");
924     }
925 
926     public static void assertCompiledByC2(Method m) {
927         TestRun.check(compiledByC2(m) != TriState.No, m + " should have been C2 compiled");
928     }
929 
930     public static void assertCompiledAtLevel(Method m, CompLevel level) {
931         TestRun.check(compiledAtLevel(m, level) != TriState.No, m + " should have been compiled at level " + level.name());
932     }
933 
934     public static void assertNotCompiled(Method m) {
935         TestRun.check(!isC1Compiled(m), m + " should not have been compiled by C1");
936         TestRun.check(!isC2Compiled(m), m + " should not have been compiled by C2");
937     }
938 
939     public static void assertCompiled(Method m) {
940         TestRun.check(compiledByC1(m) != TriState.No || compiledByC2(m) != TriState.No, m + " should have been compiled");
941     }
942 
943     private static TriState compiledByC1(Method m) {
944         TriState triState = compiledAtLevel(m, CompLevel.C1_SIMPLE);
945         if (triState != TriState.No) {
946             return triState;
947         }
948         triState = compiledAtLevel(m, CompLevel.C1_LIMITED_PROFILE);
949         if (triState != TriState.No) {
950             return triState;
951         }
952         triState = compiledAtLevel(m, CompLevel.C1_FULL_PROFILE);
953         return triState;
954     }
955 
956     private static TriState compiledByC2(Method m) {
957         return compiledAtLevel(m, CompLevel.C2);
958     }
959 
960     private static TriState compiledAtLevel(Method m, CompLevel level) {
961         if (WHITE_BOX.isMethodCompiled(m, false)) {
962             switch (level) {
963                 case C1_SIMPLE, C1_LIMITED_PROFILE, C1_FULL_PROFILE, C2 -> {
964                     if (WHITE_BOX.getMethodCompilationLevel(m, false) == level.getValue()) {
965                         return TriState.Yes;
966                     }
967                 }
968                 case ANY -> {
969                     return TriState.Yes;
970                 }
971                 default -> throw new TestRunException("compiledAtLevel() should not be called with " + level);
972             }
973         }
974         if (!USE_COMPILER || XCOMP || TEST_C1 || IGNORE_COMPILER_CONTROLS || FLIP_C1_C2 || DEOPT_BARRIERS_ALOT ||
975             (EXCLUDE_RANDOM && !WHITE_BOX.isMethodCompilable(m, level.getValue(), false))) {
976             return TriState.Maybe;
977         }
978         return TriState.No;
979     }
980 }