changeset 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 0c5304ad61bd
children d442ede93e4b
files jvmci/jdk.vm.ci.services/src/jdk/vm/ci/services/JVMCIClassLoaderFactory.java src/share/vm/classfile/classFileParser.cpp src/share/vm/classfile/javaClasses.cpp src/share/vm/classfile/systemDictionary.cpp src/share/vm/classfile/systemDictionary.hpp src/share/vm/classfile/verifier.cpp src/share/vm/jvmci/jvmciRuntime.cpp src/share/vm/prims/jvm.cpp src/share/vm/runtime/arguments.cpp
diffstat 9 files changed, 106 insertions(+), 52 deletions(-) [+]
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);
             }
         }
 
--- a/src/share/vm/classfile/classFileParser.cpp	Mon Jun 12 16:03:24 2017 +0200
+++ b/src/share/vm/classfile/classFileParser.cpp	Tue Jun 13 14:22:54 2017 +0200
@@ -1809,7 +1809,7 @@
 #if INCLUDE_JVMCI
   case vmSymbols::VM_SYMBOL_ENUM_NAME(jdk_vm_ci_hotspot_Stable_signature):
     if (_location != _in_field)   break;  // only allow for fields
-    if (!privileged && loader_data->class_loader() != SystemDictionary::jvmci_loader()) break;  // only allow in privileged code
+    if (!privileged && !SystemDictionary::in_jvmci_loader_hierarchy(loader_data->class_loader())) break;  // only allow in privileged code
     return _field_Stable;
 #endif
   case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_Stable_signature):
--- a/src/share/vm/classfile/javaClasses.cpp	Mon Jun 12 16:03:24 2017 +0200
+++ b/src/share/vm/classfile/javaClasses.cpp	Tue Jun 13 14:22:54 2017 +0200
@@ -564,7 +564,7 @@
 
   // Set protection domain also
 #if INCLUDE_JVMCI
-  if (k->class_loader() == SystemDictionary::jvmci_loader()) {
+  if (SystemDictionary::in_jvmci_loader_hierarchy(k->class_loader())) {
     // Same protection domain as for classes loaded by the boot loader
     protection_domain = Handle();
   }
--- a/src/share/vm/classfile/systemDictionary.cpp	Mon Jun 12 16:03:24 2017 +0200
+++ b/src/share/vm/classfile/systemDictionary.cpp	Tue Jun 13 14:22:54 2017 +0200
@@ -98,6 +98,7 @@
 
 #if INCLUDE_JVMCI
 oop         SystemDictionary::_jvmci_loader               = NULL;
+oop         SystemDictionary::_jvmci_loader_parent        = NULL;
 #endif
 
 // lazily initialized klass variables
@@ -131,6 +132,21 @@
   return ClassLoaderDataGraph::find_or_create(class_loader, CHECK_NULL);
 }
 
+#if INCLUDE_JVMCI
+void SystemDictionary::init_jvmci_loader(oop loader) {
+  assert(UseJVMCIClassLoader == (loader != NULL), "must be");
+  _jvmci_loader = loader;
+  if (loader != NULL) {
+    _jvmci_loader_parent = java_lang_ClassLoader::parent(loader);
+    assert(_jvmci_loader_parent == NULL ||
+           java_lang_ClassLoader::parent(_jvmci_loader_parent) == NULL,
+           "at most one parent allowed between the JVMCI loader and the boot loader");
+  }
+}
+#include "jvmci/jvmciRuntime.hpp"
+#endif
+
+
 // ----------------------------------------------------------------------------
 // debugging
 
--- a/src/share/vm/classfile/systemDictionary.hpp	Mon Jun 12 16:03:24 2017 +0200
+++ b/src/share/vm/classfile/systemDictionary.hpp	Tue Jun 13 14:22:54 2017 +0200
@@ -500,11 +500,13 @@
   // in which case it's equivalent to the boot loader
   static oop jvmci_loader()                 { return _jvmci_loader; }
 
-  // Sets the JVMCI loader. This is called at most once.
-  static void init_jvmci_loader(oop loader) {
-    assert(UseJVMCIClassLoader == (loader != NULL), "must be");
-    _jvmci_loader = loader;
+  // Determines if loader is in the JVMCI class loader hierarchy
+  static bool in_jvmci_loader_hierarchy (oop loader) {
+    return loader == NULL || loader == _jvmci_loader || loader == _jvmci_loader_parent;
   }
+
+  // Sets the JVMCI loader. This must be called at most once.
+  static void init_jvmci_loader(oop loader);
 #endif
 
   // Compute the default system loader
@@ -739,6 +741,7 @@
   static oop  _java_system_loader;
 #if INCLUDE_JVMCI
   static oop  _jvmci_loader;
+  static oop  _jvmci_loader_parent;
 #endif
 
   static bool _has_loadClassInternal;
--- a/src/share/vm/classfile/verifier.cpp	Mon Jun 12 16:03:24 2017 +0200
+++ b/src/share/vm/classfile/verifier.cpp	Tue Jun 13 14:22:54 2017 +0200
@@ -94,7 +94,7 @@
 // Methods in Verifier
 
 bool Verifier::should_verify_for(oop class_loader, bool should_verify_class) {
-  return (class_loader == NULL JVMCI_ONLY(|| class_loader == SystemDictionary::jvmci_loader()) || !should_verify_class) ?
+  return (class_loader == NULL JVMCI_ONLY(|| SystemDictionary::in_jvmci_loader_hierarchy(class_loader)) || !should_verify_class) ?
     BytecodeVerificationLocal : BytecodeVerificationRemote;
 }
 
--- a/src/share/vm/jvmci/jvmciRuntime.cpp	Mon Jun 12 16:03:24 2017 +0200
+++ b/src/share/vm/jvmci/jvmciRuntime.cpp	Tue Jun 13 14:22:54 2017 +0200
@@ -849,7 +849,7 @@
 bool JVMCIRuntime::treat_as_trivial(Method* method) {
   if (_HotSpotJVMCIRuntime_initialized) {
     oop loader = method->method_holder()->class_loader();
-    if (loader == NULL || loader == SystemDictionary::jvmci_loader()) {
+    if (SystemDictionary::in_jvmci_loader_hierarchy(loader)) {
       for (int i = 0; i < _trivial_prefixes_count; i++) {
         if (method->method_holder()->name()->starts_with(_trivial_prefixes[i])) {
           return true;
--- a/src/share/vm/prims/jvm.cpp	Mon Jun 12 16:03:24 2017 +0200
+++ b/src/share/vm/prims/jvm.cpp	Tue Jun 13 14:22:54 2017 +0200
@@ -2370,7 +2370,7 @@
 
   ResourceMark rm(THREAD);
   const char* name = k->name()->as_C_string();
-  bool system_class = k->class_loader() == NULL JVMCI_ONLY(|| SystemDictionary::jvmci_loader() == k->class_loader());
+  bool system_class = k->class_loader() == NULL JVMCI_ONLY(|| SystemDictionary::in_jvmci_loader_hierarchy(k->class_loader()));
   return JavaAssertions::enabled(name, system_class);
 
 JVM_END
--- a/src/share/vm/runtime/arguments.cpp	Mon Jun 12 16:03:24 2017 +0200
+++ b/src/share/vm/runtime/arguments.cpp	Tue Jun 13 14:22:54 2017 +0200
@@ -371,10 +371,10 @@
   // must free the string if/when no longer needed.
   char* combined_path();
 
-private:
   // Utility routines.
   static char* add_to_path(const char* path, const char* str, bool prepend);
   static char* add_jars_to_path(char* path, const char* directory);
+private:
 
   inline void reset_item_at(int index);
 
@@ -3574,27 +3574,25 @@
   scp_p->expand_endorsed();
 
 #if INCLUDE_JVMCI
+  // Append lib/boot/*.jar to boot class path
+  char bootDir[JVM_MAXPATHLEN];
+  const char* fileSep = os::file_separator();
+  jio_snprintf(bootDir, sizeof(bootDir), "%s%slib%sboot", Arguments::get_java_home(), fileSep, fileSep);
+  char* boot_suffix_path = SysClassPath::add_jars_to_path(NULL, bootDir);
+  if (boot_suffix_path != NULL) {
+    scp_p->add_suffix(boot_suffix_path);
+    scp_assembly_required = true;
+  }
+
   if (!UseJVMCIClassLoader) {
     // Append lib/jvmci/*.jar to boot class path
     char jvmciDir[JVM_MAXPATHLEN];
     const char* fileSep = os::file_separator();
     jio_snprintf(jvmciDir, sizeof(jvmciDir), "%s%slib%sjvmci", Arguments::get_java_home(), fileSep, fileSep);
-    DIR* dir = os::opendir(jvmciDir);
-    if (dir != NULL) {
-      struct dirent *entry;
-      char *dbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(jvmciDir), mtInternal);
-      while ((entry = os::readdir(dir, (dirent *) dbuf)) != NULL) {
-        const char* name = entry->d_name;
-        const char* ext = name + strlen(name) - 4;
-        if (ext > name && strcmp(ext, ".jar") == 0) {
-          char fileName[JVM_MAXPATHLEN];
-          jio_snprintf(fileName, sizeof(fileName), "%s%s%s", jvmciDir, fileSep, name);
-          scp_p->add_suffix(fileName);
-          scp_assembly_required = true;
-        }
-      }
-      FREE_C_HEAP_ARRAY(char, dbuf, mtInternal);
-      os::closedir(dir);
+    char* jvmci_path = SysClassPath::add_jars_to_path(NULL, jvmciDir);
+    if (jvmci_path != NULL) {
+      scp_p->add_suffix(jvmci_path);
+      scp_assembly_required = true;
     }
     const char* path = Arguments::get_property("jvmci.class.path.append");
     if (path != NULL) {