diff jvmci/jdk.vm.ci.services/src/jdk/vm/ci/services/JVMCIClassLoaderFactory.java @ 24140:d4858e92c9b1

[GR-4077] support Graal.SDK on the boot class path and Truffle on a class path invisible to apps but visible to JVMCI
author Doug Simon <doug.simon@oracle.com>
date Tue, 13 Jun 2017 14:22:54 +0200
parents 450e0bafd83d
children d442ede93e4b
line wrap: on
line diff
--- a/jvmci/jdk.vm.ci.services/src/jdk/vm/ci/services/JVMCIClassLoaderFactory.java	Mon Jun 12 16:03:24 2017 +0200
+++ b/jvmci/jdk.vm.ci.services/src/jdk/vm/ci/services/JVMCIClassLoaderFactory.java	Tue Jun 13 14:22:54 2017 +0200
@@ -23,9 +23,13 @@
 package jdk.vm.ci.services;
 
 import java.io.File;
+import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -33,8 +37,10 @@
 
 /**
  * Utility called from the VM to create and register a separate class loader for loading JVMCI
- * classes (i.e., those in found in lib/jvmci/*.jar) as well as those available on the class path in
- * the {@code "jvmci.class.path.append"} system property.
+ * classes (i.e., those in found in {@code <java.home>/lib/jvmci/*.jar)} as well as those available
+ * on the class path in the {@code "jvmci.class.path.append"} system property. The JVMCI class
+ * loader can optionally be given a parent other than the boot class loader as specified by
+ * {@link #getJVMCIParentClassLoader(Path)}.
  */
 class JVMCIClassLoaderFactory {
 
@@ -57,46 +63,77 @@
      * Creates a new class loader for loading JVMCI classes.
      */
     private static ClassLoader newClassLoader() {
-        URL[] urls = getJVMCIJarsUrls();
-        ClassLoader parent = null;
+        Path jvmciDir = Paths.get(VM.getSavedProperty("java.home"), "lib", "jvmci");
+        if (!Files.isDirectory(jvmciDir)) {
+            throw new InternalError(jvmciDir + " does not exist or is not a directory");
+        }
+        URL[] urls = getJVMCIJarsUrls(jvmciDir);
+        ClassLoader parent = getJVMCIParentClassLoader(jvmciDir);
         return URLClassLoader.newInstance(urls, parent);
     }
 
     /**
-     * Gets the URLs for lib/jvmci/*.jar.
+     * Creates a parent class loader for the JVMCI class loader. The class path for the loader is
+     * specified in the optional file {@code <java.home>/lib/jvmci/parentClassLoader.classpath}. If
+     * the file exists, its contents must be a well formed class path. Relative entries in the class
+     * path are resolved against {@code <java.home>/lib/jvmci}.
+     *
+     * @return {@code null} if {@code <java.home>/lib/jvmci/parentClassLoader.classpath} does not
+     *         exist otherwise a {@link URLClassLoader} constructed from the class path in the file
      */
-    private static URL[] getJVMCIJarsUrls() {
-        File javaHome = new File(VM.getSavedProperty("java.home"));
-        File lib = new File(javaHome, "lib");
-        File jvmci = new File(lib, "jvmci");
-        if (!jvmci.isDirectory()) {
-            throw new InternalError(jvmci + " does not exist or is not a directory");
+    private static ClassLoader getJVMCIParentClassLoader(Path jvmciDir) {
+        Path parentFile = jvmciDir.resolve("parentClassLoader.classpath");
+        if (Files.exists(parentFile)) {
+            String[] entries;
+            try {
+                entries = new String(Files.readAllBytes(parentFile)).trim().split(File.pathSeparator);
+            } catch (IOException e) {
+                throw new InternalError("Error reading " + parentFile.toAbsolutePath(), e);
+            }
+            URL[] urls = new URL[entries.length];
+            for (int i = 0; i < entries.length; i++) {
+                try {
+                    // If entries[i] is already absolute, it will remain unchanged
+                    urls[i] = jvmciDir.resolve(entries[i]).toFile().toURI().toURL();
+                } catch (MalformedURLException e) {
+                    throw new InternalError(e);
+                }
+            }
+            ClassLoader parent = null;
+            return URLClassLoader.newInstance(urls, parent);
         }
+        return null;
+    }
 
-        List<URL> urls = new ArrayList<>();
-        for (String fileName : jvmci.list()) {
+    /**
+     * Gets the URLs for all jar files in the {@code jvmciDir} directory as well as the entries
+     * specified by the {@code "jvmci.class.path.append"} system property.
+     */
+    private static URL[] getJVMCIJarsUrls(Path jvmciDir) {
+        String[] dirEntries = jvmciDir.toFile().list();
+
+        String append = VM.getSavedProperty("jvmci.class.path.append");
+        String[] appendEntries = append != null ? append.split(File.pathSeparator) : new String[0];
+        List<URL> urls = new ArrayList<>(dirEntries.length + appendEntries.length);
+
+        for (String fileName : dirEntries) {
             if (fileName.endsWith(".jar")) {
-                File file = new File(jvmci, fileName);
-                if (file.isDirectory()) {
+                Path path = jvmciDir.resolve(fileName);
+                if (Files.isDirectory(path)) {
                     continue;
                 }
                 try {
-                    urls.add(file.toURI().toURL());
+                    urls.add(path.toFile().toURI().toURL());
                 } catch (MalformedURLException e) {
                     throw new InternalError(e);
                 }
             }
         }
-
-        String append = VM.getSavedProperty("jvmci.class.path.append");
-        if (append != null) {
-            for (String entry : append.split(File.pathSeparator)) {
-                File file = new File(entry);
-                try {
-                    urls.add(file.toURI().toURL());
-                } catch (MalformedURLException e) {
-                    throw new InternalError(e);
-                }
+        for (String path : appendEntries) {
+            try {
+                urls.add(new File(path).toURI().toURL());
+            } catch (MalformedURLException e) {
+                throw new InternalError(e);
             }
         }