test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/MxJUnitWrapper.java
changeset 58523 fb3d408c7a7e
parent 50908 7c51db95ccb6
--- a/test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/MxJUnitWrapper.java	Wed Oct 09 20:12:46 2019 +0200
+++ b/test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/MxJUnitWrapper.java	Wed Oct 09 10:59:45 2019 -0700
@@ -29,18 +29,13 @@
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.PrintStream;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.ServiceLoader;
 import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.util.TreeSet;
 
 import org.junit.internal.JUnitSystem;
 import org.junit.internal.RealSystem;
@@ -59,6 +54,14 @@
 
 public class MxJUnitWrapper {
 
+    // Unit tests that start a JVM subprocess can use these system properties to
+    // add --add-exports and --add-opens as necessary to the JVM command line.
+    //
+    // Known usages:
+    // org.graalvm.compiler.test.SubprocessUtil.getPackageOpeningOptions()
+    public static final String OPENED_PACKAGES_PROPERTY_NAME = "com.oracle.mxtool.junit.opens";
+    public static final String EXPORTED_PACKAGES_PROPERTY_NAME = "com.oracle.mxtool.junit.exports";
+
     public static class MxJUnitConfig {
 
         public boolean verbose = false;
@@ -136,14 +139,25 @@
 
         String[] expandedArgs = expandArgs(args);
         int i = 0;
+        List<String> testSpecs = new ArrayList<>();
+        List<String> openPackagesSpecs = new ArrayList<>();
         while (i < expandedArgs.length) {
             String each = expandedArgs[i];
             if (each.charAt(0) == '-') {
                 // command line arguments
                 if (each.contentEquals("-JUnitVerbose")) {
                     config.verbose = true;
+                    config.enableTiming = true;
+                } else if (each.contentEquals("-JUnitOpenPackages")) {
+                    if (i + 1 >= expandedArgs.length) {
+                        system.out().println("Must include argument for -JUnitAddExports");
+                        System.exit(1);
+                    }
+                    openPackagesSpecs.add(expandedArgs[++i]);
                 } else if (each.contentEquals("-JUnitVeryVerbose")) {
+                    config.verbose = true;
                     config.veryVerbose = true;
+                    config.enableTiming = true;
                 } else if (each.contentEquals("-JUnitFailFast")) {
                     config.failFast = true;
                 } else if (each.contentEquals("-JUnitEnableTiming")) {
@@ -172,21 +186,35 @@
                 }
 
             } else {
-
-                try {
-                    builder.addTestSpec(each);
-                } catch (MxJUnitRequest.BuilderException ex) {
-                    system.out().println(ex.getMessage());
-                    System.exit(1);
-                }
+                testSpecs.add(each);
             }
             i++;
         }
 
-        MxJUnitRequest request = builder.build();
+        ModuleSupport moduleSupport = new ModuleSupport(system.out());
+        Set<String> opened = new TreeSet<>();
+        Set<String> exported = new TreeSet<>();
+        for (String spec : openPackagesSpecs) {
+            moduleSupport.openPackages(spec, "-JUnitOpenPackages", opened, exported);
+        }
 
-        if (System.getProperty("java.specification.version").compareTo("1.9") >= 0) {
-            addExports(request.classes, system.out());
+        for (String spec : testSpecs) {
+            try {
+                builder.addTestSpec(spec);
+            } catch (MxJUnitRequest.BuilderException ex) {
+                system.out().println(ex.getMessage());
+                System.exit(1);
+            }
+        }
+
+        MxJUnitRequest request = builder.build();
+        moduleSupport.processAddExportsAnnotations(request.classes, opened, exported);
+
+        if (!opened.isEmpty()) {
+            System.setProperty(OPENED_PACKAGES_PROPERTY_NAME, String.join(System.lineSeparator(), opened));
+        }
+        if (!exported.isEmpty()) {
+            System.setProperty(EXPORTED_PACKAGES_PROPERTY_NAME, String.join(System.lineSeparator(), exported));
         }
 
         for (RunListener p : ServiceLoader.load(RunListener.class)) {
@@ -285,8 +313,6 @@
         return result;
     }
 
-    private static final Pattern MODULE_PACKAGE_RE = Pattern.compile("([^/]+)/(.+)");
-
     private static class Timing<T> implements Comparable<Timing<T>> {
         final T subject;
         final long value;
@@ -344,93 +370,6 @@
     }
 
     /**
-     * Adds the super types of {@code cls} to {@code supertypes}.
-     */
-    private static void gatherSupertypes(Class<?> cls, Set<Class<?>> supertypes) {
-        if (!supertypes.contains(cls)) {
-            supertypes.add(cls);
-            Class<?> superclass = cls.getSuperclass();
-            if (superclass != null) {
-                gatherSupertypes(superclass, supertypes);
-            }
-            for (Class<?> iface : cls.getInterfaces()) {
-                gatherSupertypes(iface, supertypes);
-            }
-        }
-    }
-
-    /**
-     * Updates modules specified in {@code AddExport} annotations on {@code classes} to export
-     * concealed packages to the annotation classes' declaring modules.
-     */
-    private static void addExports(Set<Class<?>> classes, PrintStream out) {
-        Set<Class<?>> types = new HashSet<>();
-        for (Class<?> cls : classes) {
-            gatherSupertypes(cls, types);
-        }
-        for (Class<?> cls : types) {
-            Annotation[] annos = cls.getAnnotations();
-            for (Annotation a : annos) {
-                Class<? extends Annotation> annotationType = a.annotationType();
-                if (annotationType.getSimpleName().equals("AddExports")) {
-                    Optional<String[]> value = getElement("value", String[].class, a);
-                    if (value.isPresent()) {
-                        for (String export : value.get()) {
-                            Matcher m = MODULE_PACKAGE_RE.matcher(export);
-                            if (m.matches()) {
-                                String moduleName = m.group(1);
-                                String packageName = m.group(2);
-                                JLModule module = JLModule.find(moduleName);
-                                if (module == null) {
-                                    out.printf("%s: Cannot find module named %s specified in \"AddExports\" annotation: %s%n", cls.getName(), moduleName, a);
-                                } else {
-                                    if (packageName.equals("*")) {
-                                        module.exportAllPackagesTo(JLModule.fromClass(cls));
-                                    } else {
-                                        module.addExports(packageName, JLModule.fromClass(cls));
-                                        module.addOpens(packageName, JLModule.fromClass(cls));
-                                    }
-                                }
-                            } else {
-                                out.printf("%s: Ignoring \"AddExports\" annotation with value not matching <module>/<package> pattern: %s%n", cls.getName(), a);
-                            }
-                        }
-                    } else {
-                        out.printf("%s: Ignoring \"AddExports\" annotation without `String value` element: %s%n", cls.getName(), a);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Gets the value of the element named {@code name} of type {@code type} from {@code annotation}
-     * if present.
-     *
-     * @return the requested element value wrapped in an {@link Optional} or
-     *         {@link Optional#empty()} if {@code annotation} has no element named {@code name}
-     * @throws AssertionError if {@code annotation} has an element of the given name but whose type
-     *             is not {@code type} or if there's some problem reading the value via reflection
-     */
-    private static <T> Optional<T> getElement(String name, Class<T> type, Annotation annotation) {
-        Class<? extends Annotation> annotationType = annotation.annotationType();
-        Method valueAccessor;
-        try {
-            valueAccessor = annotationType.getMethod(name);
-            if (!valueAccessor.getReturnType().equals(type)) {
-                throw new AssertionError(String.format("Element %s of %s is of type %s, not %s ", name, annotationType.getName(), valueAccessor.getReturnType().getName(), type.getName()));
-            }
-        } catch (NoSuchMethodException e) {
-            return Optional.empty();
-        }
-        try {
-            return Optional.of(type.cast(valueAccessor.invoke(annotation)));
-        } catch (Exception e) {
-            throw new AssertionError(String.format("Could not read %s element from %s", name, annotation), e);
-        }
-    }
-
-    /**
      * Expand any arguments starting with @ and return the resulting argument array.
      *
      * @return the expanded argument array