make/tools/classanalyzer/src/com/sun/classanalyzer/PlatformModuleBuilder.java

Print this page

        

@@ -20,199 +20,359 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
 package com.sun.classanalyzer;
 
+import com.sun.classanalyzer.ModuleInfo.Dependence;
+import static com.sun.classanalyzer.PlatformModuleBuilder.PlatformModuleNames.*;
+
 import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.IOException;
+import java.util.*;
 
-import java.util.Collection;
-import java.util.Deque;
-import java.util.LinkedList;
-import java.util.Properties;
-import java.util.Set;
-import java.util.TreeSet;
-import static com.sun.classanalyzer.Module.*;
-
 /**
- * Platform module.
+ * A platform module builder for JDK.  JDK's boot module, base module,
+ * and JRE tools module must be defined in the modules.config files.
+ * The platform module builder will create the following platform
+ * modules in addition to the ones specified in the configuration files:
+ * - JDK and JRE aggregator modules
+ * - one jdk.<m> aggregator module for each sun.<m> module to
+ *   reexport its public APIs
  *
- * The name of the platform modules starts with either "jdk." or "sun.".
- * All sun.* and jdk.boot are local modules.  Any requesting module
- * of a local module has to be explicitly permitted.
- *
- * The input module config files can define "sun.*" and "jdk.*"
- * modules.  For any sun.* module, it will have a corresponding
- * non-local platform module that is defined for application modules
- * to require.
- *
- * The tool will create the following platform modules:
- * 1) jdk.<name> for each sun.<name> module
- * 2) jdk module - represents the entire JDK
- * 3) jdk.jre module - represents the entire JRE
- *
+ * @author Mandy Chung
  */
-public class Platform {
+public class PlatformModuleBuilder extends ModuleBuilder {
+    /**
+     * Platform modules that must be defined in the modules.properties
+     */
+    static class PlatformModuleNames {
+        static final String BOOT_MODULE =
+                getValue("platform.boot.module");
+        static final String BASE_MODULE =
+                getValue("platform.base.module");
+        static final String JDK_MODULE =
+                getValue("platform.jdk.module");
+        static final String JRE_MODULE =
+                getValue("platform.jre.module");
+        static final String JRE_TOOLS_MODULE =
+                getValue("platform.jre.tools.module");
 
-    static final String DEFAULT_BOOT_MODULE = "jdk.boot";
-    static final String JDK_BASE_MODULE = "jdk.base";
-    // platform modules created but not defined in the input module configs.
-    static final String JDK_MODULE = "jdk";
-    static final String JRE_MODULE = "jdk.jre";
-    static final String LEGACY_MODULE = "jdk.legacy";
-    // the following modules are expected to be defined in
-    // the input module config files.
-    static final String JDK_TOOLS = "jdk.tools";
-    static final String JRE_TOOLS = "jdk.tools.jre";
-    static final String JDK_BASE_TOOLS = "jdk.tools.base";
-    static final String JDK_LANGTOOLS = "jdk.langtools";
-
-    static boolean isBootModule(String name) {
-        return name.equals(DEFAULT_BOOT_MODULE);
+        private static String getValue(String key) {
+            String value = Module.getModuleProperty(key);
+            if (value == null || value.isEmpty()) {
+                throw new RuntimeException("Null or empty module property: " + key);
     }
-    private static BootModule bootModule;
-
-    static Module createBootModule(ModuleConfig config) {
-        bootModule = new BootModule(config);
-        return bootModule;
+            return value;
     }
+    }
 
-    static Module bootModule() {
-        return bootModule;
+    private BootModule bootModule;
+    private final PlatformModule jdkModule;
+    private final PlatformModule jreModule;
+
+    public PlatformModuleBuilder(List<String> configs, String version)
+            throws IOException {
+        this(configs, null, true, version);
     }
-    private static Module jdkBaseModule;
 
-    static Module jdkBaseModule() {
-        if (jdkBaseModule == null) {
-            jdkBaseModule = findModule(JDK_BASE_MODULE);
+    public PlatformModuleBuilder(List<String> configs,
+                                 List<String> depconfigs,
+                                 boolean merge,
+                                 String version)
+            throws IOException {
+        super(null, depconfigs, merge, version);
+
+        Module.setBaseModule(BASE_MODULE);
+
+        // create modules based on the input config files
+        for (String file : configs) {
+            for (ModuleConfig mconfig : ModuleConfig.readConfigurationFile(file)) {
+                newModule(mconfig);
         }
-        return jdkBaseModule;
     }
-    private static Module jdkBaseToolModule;
 
-    static Module jdkBaseToolModule() {
-        if (jdkBaseToolModule == null) {
-            jdkBaseToolModule = findModule(JDK_BASE_TOOLS);
+        // Create the full jdk and jre modules
+        jdkModule = (PlatformModule) newModule(JDK_MODULE);
+        jreModule = (PlatformModule) newModule(JRE_MODULE);
         }
-        return jdkBaseToolModule;
-    }
-    private static Module jdkModule;
-    private static Module jreModule;
-    private static Module legacyModule;
 
-    static Module jdkModule() {
-        return jdkModule;
+    @Override
+    public Module newModule(ModuleConfig mconfig) {
+        return addPlatformModule(mconfig);
     }
 
-    static Module jreModule() {
-        return jreModule;
+    @Override
+    public void run() throws IOException {
+        // assign classes and resource files to the modules
+        // group fine-grained modules per configuration files
+        buildModules();
+
+        // build public jdk modules to reexport sun.* modules
+        buildJDKModules();
+
+        // generate package information
+        buildPackageInfos();
+
+        // analyze cross-module dependencies and generate ModuleInfo
+        buildModuleInfos();
+
+        // ## Hack: add local to all requires
+        for (Module m : Module.getAllModules()) {
+            if (m.isTopLevel()) {
+                PlatformModule pm = (PlatformModule) m;
+                if (pm.isBootConnected()) {
+                    for (Dependence d : pm.getModuleInfo().requires()) {
+                        d.addModifier(Dependence.Modifier.LOCAL);
     }
+                }
+            }
+        }
+    }
 
-    static Module legacyModule() {
-        return legacyModule;
+    private void buildJDKModules() {
+        Set<PlatformModule> modules = new LinkedHashSet<PlatformModule>();
+        PlatformModule jreToolModule = (PlatformModule)
+                Module.findModule(JRE_TOOLS_MODULE);
+
+        for (Module m : Module.getAllModules()) {
+            if (m.isTopLevel()) {
+                PlatformModule pm = (PlatformModule) m;
+                modules.add(pm);
     }
+        }
 
-    private static Module addPlatformModule(String name, String mainClass) {
+        // set exporter
+        for (PlatformModule pm : modules) {
+            PlatformModule exporter = pm;
+            String name = pm.name();
+            if (name.startsWith("sun.")) {
+                // create an aggregate module for each sun.* module
+                String mn = name.replaceFirst("sun", "jdk");
+                String mainClassName =
+                        pm.mainClass() == null ? null : pm.mainClass().getClassName();
+
+                PlatformModule rm = (PlatformModule) Module.findModule(mn);
+                if (rm != null) {
+                    if (pm.mainClass() != rm.mainClass()) {
+                        // propagate the main class to its aggregator module
+                        rm.setMainClass(mainClassName);
+                    }
+                    exporter = rm;
+                } else if (pm.hasPlatformAPIs()) {
         ModuleConfig config = null;
         try {
-            config = new ModuleConfig(name, mainClass);
-            return Module.addModule(config);
+                        config = new ModuleConfig(mn, mainClassName);
         } catch (IOException ex) {
             throw new RuntimeException(ex);
         }
+                    exporter = addPlatformModule(config);
     }
 
-    static boolean isAggregator(String name) {
-        return name.equals(JDK_MODULE) ||
-                name.equals(JRE_MODULE) ||
-                name.equals(JDK_TOOLS) ||
-                name.equals(JRE_TOOLS) ||
-                name.equals(JDK_BASE_TOOLS) ||
-                name.equals(LEGACY_MODULE) ||
-                name.startsWith(JDK_LANGTOOLS);
+                if (pm != exporter) {
+                    pm.reexportBy(exporter);
     }
+            }
+        }
 
-    // returns the module that is used by the requires statement
-    // in other module's module-info
-    static Module toRequiresModule(Module m) {
-        Module moduleForRequires = m;
-        if (m == bootModule()) {
-            moduleForRequires = jdkBaseModule();
-        } else if (m.name().startsWith("sun.")) {
-            // create an aggregate module for each sun.* module
-            String mn = m.name().replaceFirst("sun", "jdk");
-            String mainClassName = m.mainClass() == null ? null : m.mainClass().getClassName();
+        // base module to reexport boot module
+        bootModule.reexportBy((PlatformModule) Module.findModule(BASE_MODULE));
 
-            Module rm = findModule(mn);
-            if (rm != null) {
-                if (rm.mainClass() != m.mainClass()) {
-                    throw new RuntimeException(mn +
-                            " module already exists but mainClass not matched");
+        // set up the jdk, jdk.jre and jdk.legacy modules
+        for (Module m : Module.getAllModules()) {
+            if (m.isTopLevel()) {
+                PlatformModule pm = (PlatformModule) m;
+                String name = pm.name();
+                if (name.startsWith("jdk.") || name.startsWith("sun.")) {
+                    if (pm != jdkModule && pm != jreModule) {
+                        Module exp = pm.exporter(jdkModule);
+                        // the "jdk" module requires all platform modules (public ones)
+                        jdkModule.config().export(exp);
+                        if (pm.isBootConnected() || pm == jreToolModule) {
+                            // add all modules that are strongly connected to jdk.boot to JRE
+                            jreModule.config().export(exp);
                 }
-                return rm;
             }
+                }
+            }
+        }
 
-            if (m.hasPlatformAPIs()) {
-                ModuleConfig config = null;
-                try {
-                    config = new ModuleConfig(mn, mainClassName);
-                } catch (IOException ex) {
-                    throw new RuntimeException(ex);
                 }
-                moduleForRequires = Module.addModule(config);
-                moduleForRequires.addRequiresModule(m);
+
+    /*
+     * Returns an ordered list of platform modules according to
+     * their dependencies with jdk.boot always be the first.
+     */
+    @Override
+    public Set<Module> getModules() {
+        Set<Module> modules = new LinkedHashSet<Module>();
+        // put the boot module first
+        modules.add(bootModule);
+        Module base = Module.findModule(BASE_MODULE);
+        modules.addAll(base.getModuleInfo().dependences(
+                new Dependence.Filter() {
+                    @Override
+                    public boolean accept(Dependence d) {
+                        return !d.isOptional();
             }
+                }));
+        modules.addAll(jdkModule.getModuleInfo().dependences(null));
+        for (Module m : Module.getAllModules()) {
+            if (m.isTopLevel() && !modules.contains(m)) {
+                modules.addAll(m.getModuleInfo().dependences(null));
         }
-        return moduleForRequires;
     }
+        return modules;
+    }
 
-    static void fixupPlatformModules() {
-        // Create the full jdk and jre modules
-        jdkModule = addPlatformModule(JDK_MODULE, null /* no main class */);
-        jreModule = addPlatformModule(JRE_MODULE, null /* no main class */);
+    void readNonCorePackagesFrom(String nonCorePkgsFile) throws IOException {
+        PlatformPackage.addNonCorePkgs(nonCorePkgsFile);
+    }
 
-        Module jreTools = findModule(JRE_TOOLS);
-        Module jdkTools = findModule(JDK_TOOLS);
-        Module jdkBaseTools = findModule(JDK_BASE_TOOLS);
+    private PlatformModule addPlatformModule(ModuleConfig config) {
+        PlatformModule m;
+        if (config.module.equals(BOOT_MODULE)) {
+            bootModule = new BootModule(config);
+            m = bootModule;
+        } else {
+            m = new PlatformModule(config);
+        }
+        Module.addModule(m);
+        return m;
+    }
 
-        for (Module m : getTopLevelModules()) {
-            String mn = m.name();
+    public class PlatformModule extends Module {
+        private Module exporter;  // module that reexports this platform module
+        private String mainClass;
+        public PlatformModule(ModuleConfig config) {
+            super(config);
+            this.exporter = this;
+            this.mainClass = config.mainClass();
+        }
 
-            // initialize module-info
-            m.fixupModuleInfo();
+        // support for incremental build
+        // an aggregate module "jdk.*" is not defined in modules.config 
+        // files but created by the platform module builder
+        // Set to the main class of sun.* module
+        void setMainClass(String classname) {
+            String mn = name();
+            if (!mn.startsWith("jdk") || !isEmpty()) {
+                throw new RuntimeException("module " + name() +
+                    " not an aggregator");
+            }
 
-            // set up the jdk, jdk.jre and jdk.legacy modules
-            if (mn.startsWith("jdk.") || mn.startsWith("sun.")) {
-                Module req = m.toRequiredModule();
+            if (classname == null)
+                throw new RuntimeException("Null main class for module " + name());
 
-                if (!m.isAggregator() && !mn.equals(JDK_MODULE) && !mn.equals(JRE_MODULE)) {
-                    // all platform modules are required jdk module
-                    jdkModule.addRequiresModule(req);
-                    if (m.isBootConnected() || mn.equals(JRE_TOOLS)) {
-                        // add all modules that are strongly connected to jdk.boot to JRE
-                        jreModule.addRequiresModule(req);
+            mainClass = classname;
                     }
+        
+        @Override
+        Klass mainClass() {
+            if (mainClass != null)
+                return Klass.findKlass(mainClass);
+            else
+                return null;
                 }
+
+        @Override
+        boolean allowEmpty() {
+            return this == jdkModule || this == jreModule || super.allowEmpty();
+        }
+
+        // requires local for JRE modules that are strongly
+        // connected with the boot module
+        boolean isBootConnected() {
+            // ## should it check for local?
+            return config().requires.containsKey(BOOT_MODULE);
+        }
+
+        private int platformAPIs;
+        boolean hasPlatformAPIs() {
+            platformAPIs = 0;
+            Visitor<Void, PlatformModule> v = new Visitor<Void, PlatformModule>() {
+                public Void visitClass(Klass k, PlatformModule pm) {
+                    if (PlatformPackage.isOfficialClass(k.getClassName())) {
+                        pm.platformAPIs++;
+                    }
+                    return null;
+                }
+
+                public Void visitResource(ResourceFile r, PlatformModule pm) {
+                    return null;
+                }
+            };
+
+            this.visit(v, this);
+            return platformAPIs > 0;
+        }
+
+        // returns the module that is used by the requires statement
+        // in other module's module-info
+        @Override
+        public Module exporter(Module from) {
+            PlatformModule pm = (PlatformModule) from;
+            if (pm.isBootConnected()) {
+                // If it's a local module requiring jdk.boot, retain
+                // the original requires; otherwise, use its external
+                // module
+                return this;
             } else {
-                Trace.trace("Non-platform module: %s%n", m.name());
+                return exporter;
             }
         }
-        // fixup the base module to include optional dependences from boot
-        // ## It adds jndi, logging, and xml optional dependences
-        // bootModule.fixupBase();
+
+        void reexportBy(PlatformModule pm) {
+            exporter = pm;
+            // sun.<m> permits jdk.<m>
+            this.config().addPermit(pm);
+            // jdk.<m> requires public sun.<m>;
+            pm.config().export(this);
     }
+    }
+
+    public class BootModule extends PlatformModule {
+        BootModule(ModuleConfig config) {
+            super(config);
+        }
+
+        @Override
+        boolean isBootConnected() {
+            return true;
+        }
+    }
+
+    static class PlatformPackage {
+
     private static String[] corePkgs = new String[]{
         "java", "javax",
         "org.omg", "org.w3c.dom",
         "org.xml.sax", "org.ietf.jgss"
     };
     private static Set<String> nonCorePkgs = new TreeSet<String>();
 
-    static void addNonCorePkgs(String file) throws FileNotFoundException, IOException {
+        static boolean isOfficialClass(String classname) {
+            for (String pkg : corePkgs) {
+                if (classname.startsWith(pkg + ".")) {
+                    return true;
+                }
+            }
+
+            // TODO: include later
+            /*
+            for (String pkg : nonCorePkgs) {
+            if (classname.startsWith(pkg + ".")) {
+            return true;
+            }
+            }
+             */
+            return false;
+        }
+
+        // process a properties file listing the non core packages
+        static void addNonCorePkgs(String file) throws IOException {
         File f = new File(file);
         Properties props = new Properties();
         BufferedReader reader = null;
 
         try {

@@ -238,12 +398,12 @@
                     for (String v : ss) {
                         values.add(v.trim());
                     }
                     continue;
                 }
-                if (pval.startsWith("java.") || pval.startsWith("javax") ||
-                        pval.startsWith("com.") || pval.startsWith("org.")) {
+                    if (pval.startsWith("java.") || pval.startsWith("javax")
+                            || pval.startsWith("com.") || pval.startsWith("org.")) {
                     nonCorePkgs.add(pval);
                 } else {
                     throw new RuntimeException("Invalid non core package: " + pval);
                 }
             }

@@ -251,70 +411,7 @@
             if (reader != null) {
                 reader.close();
             }
         }
     }
-
-    static boolean isPlatformAPI(String classname) {
-        for (String pkg : corePkgs) {
-            if (classname.startsWith(pkg + ".")) {
-                return true;
             }
-        }
-        return false;
-    }
-
-    static boolean isNonCoreAPI(String pkgName) {
-        for (String pkg : nonCorePkgs) {
-            if (pkgName.startsWith(pkg)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    static class BootModule extends Module {
-
-        BootModule(ModuleConfig config) {
-            super(config);
-        }
-
-        Collection<Dependency> dependences() {
-            Set<Dependency> result = new TreeSet<Dependency>();
-            for (Dependency d : dependents()) {
-                // filter out optional dependences from jdk.boot module
-                if (!d.optional) {
-                    result.add(d);
-                }
-            }
-            return result;
-        }
-
-        Module toRequiresModule() {
-            return jdkBaseModule();
-        }
-
-        boolean isBootConnected() {
-            return true;
-        }
-
-        boolean requirePermits() {
-            return true;
-        }
-
-        void fixupBase() {
-            // fixup jdk.boot optional dependences
-            for (Dependency d : dependents()) {
-                if (d.optional) {
-                    Module m = d.module().toRequiredModule();
-                    jdkBaseModule().addRequiresModule(m);
-                    Trace.trace("add requires %s to %s%n", m, jdkBaseModule().name());
-                    if (m != d.module()) {
-                        m.permits().remove(this);
-                    }
-
-                }
-            }
-
-        }
-    }
 }