changeset 40013:943cf01a6b82

8154239: -Xbootclasspath/a breaks exploded build Summary: Correct exploded modules build system class path search for the boot loader Reviewed-by: acorn, ccheung, hseigel, jiangli
author lfoltan
date Tue, 26 Jul 2016 10:29:27 -0400
parents f69cfe79fe98
children 5cdf4815deca bf6fcd467a7b
files hotspot/src/share/vm/classfile/classLoader.cpp hotspot/src/share/vm/classfile/classLoader.hpp hotspot/src/share/vm/classfile/classLoaderExt.hpp hotspot/src/share/vm/classfile/modules.cpp hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp hotspot/src/share/vm/classfile/systemDictionary.cpp hotspot/src/share/vm/memory/filemap.cpp hotspot/src/share/vm/runtime/arguments.cpp hotspot/src/share/vm/runtime/arguments.hpp hotspot/src/share/vm/runtime/init.cpp hotspot/src/share/vm/runtime/os.cpp
diffstat 11 files changed, 340 insertions(+), 314 deletions(-) [+]
line wrap: on
line diff
--- a/hotspot/src/share/vm/classfile/classLoader.cpp	Tue Jul 26 11:04:20 2016 +0200
+++ b/hotspot/src/share/vm/classfile/classLoader.cpp	Tue Jul 26 10:29:27 2016 -0400
@@ -141,11 +141,11 @@
 PerfCounter*    ClassLoader::_load_instance_class_failCounter = NULL;
 
 GrowableArray<ModuleClassPathList*>* ClassLoader::_xpatch_entries = NULL;
-ClassPathEntry* ClassLoader::_first_entry        = NULL;
-ClassPathEntry* ClassLoader::_last_entry         = NULL;
+GrowableArray<ModuleClassPathList*>* ClassLoader::_exploded_entries = NULL;
+ClassPathEntry* ClassLoader::_jrt_entry = NULL;
+ClassPathEntry* ClassLoader::_first_append_entry = NULL;
+ClassPathEntry* ClassLoader::_last_append_entry  = NULL;
 int             ClassLoader::_num_entries        = 0;
-ClassPathEntry* ClassLoader::_first_append_entry = NULL;
-bool            ClassLoader::_has_jimage = false;
 #if INCLUDE_CDS
 GrowableArray<char*>* ClassLoader::_boot_modules_array = NULL;
 GrowableArray<char*>* ClassLoader::_platform_modules_array = NULL;
@@ -508,7 +508,7 @@
 #endif
 
       } else {
-        PackageEntry* package_entry = get_package_entry(name, ClassLoaderData::the_null_class_loader_data(), THREAD);
+        PackageEntry* package_entry = get_package_entry(name, ClassLoaderData::the_null_class_loader_data(), CHECK_NULL);
         if (package_entry != NULL) {
           ResourceMark rm;
           // Get the module name
@@ -651,7 +651,6 @@
 #endif
 
 void ClassLoader::setup_bootstrap_search_path() {
-  assert(_first_entry == NULL, "should not setup bootstrap class search path twice");
   const char* sys_class_path = Arguments::get_sysclasspath();
   const char* java_class_path = Arguments::get_appclasspath();
   if (PrintSharedArchiveAndExit) {
@@ -694,7 +693,10 @@
   GrowableArray<ModuleXPatchPath*>* xpatch_args = Arguments::get_xpatchprefix();
   int num_of_entries = xpatch_args->length();
 
-  // Set up the boot loader's xpatch_entries list
+  assert(!DumpSharedSpaces, "DumpSharedSpaces not supported with -Xpatch");
+  assert(!UseSharedSpaces, "UseSharedSpaces not supported with -Xpatch");
+
+  // Set up the boot loader's _xpatch_entries list
   _xpatch_entries = new (ResourceObj::C_HEAP, mtModule) GrowableArray<ModuleClassPathList*>(num_of_entries, true);
 
   for (int i = 0; i < num_of_entries; i++) {
@@ -742,10 +744,9 @@
 }
 
 void ClassLoader::setup_search_path(const char *class_path, bool bootstrap_search) {
-  int offset = 0;
   int len = (int)strlen(class_path);
   int end = 0;
-  bool mark_append_entry = false;
+  bool set_base_piece = bootstrap_search;
 
   // Iterate over class path entries
   for (int start = 0; start < len; start = end) {
@@ -754,21 +755,45 @@
     }
     EXCEPTION_MARK;
     ResourceMark rm(THREAD);
-    mark_append_entry = (mark_append_entry ||
-      (bootstrap_search && (start == Arguments::bootclassloader_append_index())));
     char* path = NEW_RESOURCE_ARRAY(char, end - start + 1);
     strncpy(path, &class_path[start], end - start);
     path[end - start] = '\0';
-    update_class_path_entry_list(path, false, mark_append_entry, false, bootstrap_search);
 
-    // Check on the state of the boot loader's append path
-    if (mark_append_entry && (_first_append_entry == NULL)) {
-      // Failure to mark the first append entry, most likely
-      // due to a non-existent path. Record the next entry
-      // as the first boot loader append entry.
-      mark_append_entry = true;
+    // The first time through the bootstrap_search setup, it must be determined
+    // what the base or core piece of the boot loader search is.  Either a java runtime
+    // image is present or this is an exploded module build situation.
+    if (set_base_piece) {
+      assert(string_ends_with(path, MODULES_IMAGE_NAME) || string_ends_with(path, "java.base"),
+             "Incorrect boot loader search path, no java runtime image or java.base exploded build");
+      struct stat st;
+      if (os::stat(path, &st) == 0) {
+        // Directory found
+        Thread* THREAD = Thread::current();
+        ClassPathEntry* new_entry = create_class_path_entry(path, &st, false, false, CHECK);
+
+        // Check for a jimage
+        if (Arguments::has_jimage()) {
+          assert(_jrt_entry == NULL, "should not setup bootstrap class search path twice");
+          assert(new_entry != NULL && new_entry->is_jrt(), "No java runtime image present");
+          _jrt_entry = new_entry;
+          ++_num_entries;
+#if INCLUDE_CDS
+          if (DumpSharedSpaces) {
+            JImageFile *jimage = _jrt_entry->jimage();
+            assert(jimage != NULL, "No java runtime image file present");
+            ClassLoader::initialize_module_loader_map(jimage);
+          }
+#endif
+        }
+      } else {
+        // If path does not exist, exit
+        vm_exit_during_initialization("Unable to establish the boot loader search path", path);
+      }
+      set_base_piece = false;
     } else {
-      mark_append_entry = false;
+      // Every entry on the system boot class path after the initial base piece,
+      // which is set by os::set_boot_path(), is considered an appended entry.
+      update_class_path_entry_list(path, false, bootstrap_search);
     }
 
 #if INCLUDE_CDS
@@ -782,6 +807,45 @@
   }
 }
 
+// During an exploded modules build, each module defined to the boot loader
+// will be added to the ClassLoader::_exploded_entries array.
+void ClassLoader::add_to_exploded_build_list(Symbol* module_sym, TRAPS) {
+  assert(!ClassLoader::has_jrt_entry(), "Exploded build not applicable");
+
+  // Set up the boot loader's _exploded_entries list
+  if (_exploded_entries == NULL) {
+    _exploded_entries = new (ResourceObj::C_HEAP, mtModule) GrowableArray<ModuleClassPathList*>(EXPLODED_ENTRY_SIZE, true);
+  }
+
+  // Find the module's symbol
+  ResourceMark rm(THREAD);
+  const char *module_name = module_sym->as_C_string();
+  const char *home = Arguments::get_java_home();
+  const char file_sep = os::file_separator()[0];
+  // 10 represents the length of "modules" + 2 file separators + \0
+  size_t len = strlen(home) + strlen(module_name) + 10;
+  char *path = NEW_C_HEAP_ARRAY(char, len, mtModule);
+  jio_snprintf(path, len, "%s%cmodules%c%s", home, file_sep, file_sep, module_name);
+
+  struct stat st;
+  if (os::stat(path, &st) == 0) {
+    // Directory found
+    ClassPathEntry* new_entry = create_class_path_entry(path, &st, false, false, CHECK);
+
+    // If the path specification is valid, enter it into this module's list.
+    // There is no need to check for duplicate modules in the exploded entry list,
+    // since no two modules with the same name can be defined to the boot loader.
+    // This is checked at module definition time in Modules::define_module.
+    if (new_entry != NULL) {
+      ModuleClassPathList* module_cpl = new ModuleClassPathList(module_sym);
+      module_cpl->add_to_list(new_entry);
+      _exploded_entries->push(module_cpl);
+      log_info(class, load)("path: %s", path);
+    }
+  }
+  FREE_C_HEAP_ARRAY(char, path);
+}
+
 ClassPathEntry* ClassLoader::create_class_path_entry(const char *path, const struct stat* st,
                                                      bool throw_exception,
                                                      bool is_boot_append, TRAPS) {
@@ -872,21 +936,9 @@
   return NULL;
 }
 
-// The boot class loader must adhere to specfic visibility rules.
-// Prior to loading a class in a named package, the package is checked
-// to see if it is in a module defined to the boot loader. If the
-// package is not in a module defined to the boot loader, the class
-// must be loaded only in the boot loader's append path, which
-// consists of [-Xbootclasspath/a]; [jvmti appended entries]
-void ClassLoader::set_first_append_entry(ClassPathEntry *new_entry) {
-  if (_first_append_entry == NULL) {
-    _first_append_entry = new_entry;
-  }
-}
-
 // returns true if entry already on class path
 bool ClassLoader::contains_entry(ClassPathEntry *entry) {
-  ClassPathEntry* e = _first_entry;
+  ClassPathEntry* e = _first_append_entry;
   while (e != NULL) {
     // assume zip entries have been canonicalized
     if (strcmp(entry->name(), e->name()) == 0) {
@@ -899,41 +951,24 @@
 
 void ClassLoader::add_to_list(ClassPathEntry *new_entry) {
   if (new_entry != NULL) {
-    if (_last_entry == NULL) {
-      _first_entry = _last_entry = new_entry;
+    if (_last_append_entry == NULL) {
+      assert(_first_append_entry == NULL, "boot loader's append class path entry list not empty");
+      _first_append_entry = _last_append_entry = new_entry;
     } else {
-      _last_entry->set_next(new_entry);
-      _last_entry = new_entry;
+      _last_append_entry->set_next(new_entry);
+      _last_append_entry = new_entry;
     }
   }
-  _num_entries ++;
-}
-
-void ClassLoader::prepend_to_list(ClassPathEntry *new_entry) {
-  if (new_entry != NULL) {
-    if (_last_entry == NULL) {
-      _first_entry = _last_entry = new_entry;
-    } else {
-      new_entry->set_next(_first_entry);
-      _first_entry = new_entry;
-    }
-  }
-  _num_entries ++;
+  _num_entries++;
 }
 
 void ClassLoader::add_to_list(const char *apath) {
-  update_class_path_entry_list((char*)apath, false, false, false, false);
-}
-
-void ClassLoader::prepend_to_list(const char *apath) {
-  update_class_path_entry_list((char*)apath, false, false, true, false);
+  update_class_path_entry_list((char*)apath, false, false);
 }
 
 // Returns true IFF the file/dir exists and the entry was successfully created.
 bool ClassLoader::update_class_path_entry_list(const char *path,
                                                bool check_for_duplicates,
-                                               bool mark_append_entry,
-                                               bool prepend_entry,
                                                bool is_boot_append,
                                                bool throw_exception) {
   struct stat st;
@@ -946,19 +981,10 @@
       return false;
     }
 
-    // Ensure that the first boot loader append entry will always be set correctly.
-    assert((!mark_append_entry ||
-            (mark_append_entry && (!check_for_duplicates || !contains_entry(new_entry)))),
-           "failed to mark boot loader's first append boundary");
-
     // Do not reorder the bootclasspath which would break get_system_package().
     // Add new entry to linked list
-
     if (!check_for_duplicates || !contains_entry(new_entry)) {
-      ClassLoaderExt::add_class_path_entry(path, check_for_duplicates, new_entry, prepend_entry);
-      if (mark_append_entry) {
-        set_first_append_entry(new_entry);
-      }
+      ClassLoaderExt::add_class_path_entry(path, check_for_duplicates, new_entry);
     }
     return true;
   } else {
@@ -971,30 +997,47 @@
   }
 }
 
+static void print_module_entry_table(const GrowableArray<ModuleClassPathList*>* const module_list) {
+  ResourceMark rm;
+  int num_of_entries = module_list->length();
+  for (int i = 0; i < num_of_entries; i++) {
+    ClassPathEntry* e;
+    ModuleClassPathList* mpl = module_list->at(i);
+    tty->print("%s=", mpl->module_name()->as_C_string());
+    e = mpl->module_first_entry();
+    while (e != NULL) {
+      tty->print("%s", e->name());
+      e = e->next();
+      if (e != NULL) {
+        tty->print("%s", os::path_separator());
+      }
+    }
+    tty->print(" ;");
+  }
+}
+
 void ClassLoader::print_bootclasspath() {
   ClassPathEntry* e;
   tty->print("[bootclasspath= ");
 
   // Print -Xpatch module/path specifications first
   if (_xpatch_entries != NULL) {
-    ResourceMark rm;
-    int num_of_entries = _xpatch_entries->length();
-    for (int i = 0; i < num_of_entries; i++) {
-      ModuleClassPathList* mpl = _xpatch_entries->at(i);
-      tty->print("%s=", mpl->module_name()->as_C_string());
-      e = mpl->module_first_entry();
-      while (e != NULL) {
-        tty->print("%s", e->name());
-        e = e->next();
-        if (e != NULL) {
-          tty->print("%s", os::path_separator());
-        }
-      }
-      tty->print(" ;");
+    print_module_entry_table(_xpatch_entries);
+  }
+
+  // [jimage | exploded modules build]
+  if (has_jrt_entry()) {
+    // Print the location of the java runtime image
+    tty->print("%s ;", _jrt_entry->name());
+  } else {
+    // Print exploded module build path specifications
+    if (_exploded_entries != NULL) {
+      print_module_entry_table(_exploded_entries);
     }
   }
 
-  e = _first_entry;
+  // appended entries
+  e = _first_append_entry;
   while (e != NULL) {
     tty->print("%s ;", e->name());
     e = e->next();
@@ -1298,6 +1341,60 @@
   return file_name;
 }
 
+// Search either the xpatch or exploded build entries for class
+ClassFileStream* ClassLoader::search_module_entries(const GrowableArray<ModuleClassPathList*>* const module_list,
+                                                    const char* const class_name, const char* const file_name, TRAPS) {
+  ClassFileStream* stream = NULL;
+
+  // Find the class' defining module in the boot loader's module entry table
+  PackageEntry* pkg_entry = get_package_entry(class_name, ClassLoaderData::the_null_class_loader_data(), CHECK_NULL);
+  ModuleEntry* mod_entry = (pkg_entry != NULL) ? pkg_entry->module() : NULL;
+
+  // If the module system has not defined java.base yet, then
+  // classes loaded are assumed to be defined to java.base.
+  // When java.base is eventually defined by the module system,
+  // all packages of classes that have been previously loaded
+  // are verified in ModuleEntryTable::verify_javabase_packages().
+  if (!Universe::is_module_initialized() &&
+      !ModuleEntryTable::javabase_defined() &&
+      mod_entry == NULL) {
+    mod_entry = ModuleEntryTable::javabase_module();
+  }
+
+  // The module must be a named module
+  if (mod_entry != NULL && mod_entry->is_named()) {
+    int num_of_entries = module_list->length();
+    const Symbol* class_module_name = mod_entry->name();
+
+    // Loop through all the modules in either the xpatch or exploded entries looking for module
+    for (int i = 0; i < num_of_entries; i++) {
+      ModuleClassPathList* module_cpl = module_list->at(i);
+      Symbol* module_cpl_name = module_cpl->module_name();
+
+      if (module_cpl_name->fast_compare(class_module_name) == 0) {
+        // Class' module has been located, attempt to load
+        // the class from the module's ClassPathEntry list.
+        ClassPathEntry* e = module_cpl->module_first_entry();
+        while (e != NULL) {
+          stream = e->open_stream(file_name, CHECK_NULL);
+          // No context.check is required since CDS is not supported
+          // for an exploded modules build or if -Xpatch is specified.
+          if (NULL != stream) {
+            return stream;
+          }
+          e = e->next();
+        }
+        // If the module was located, break out even if the class was not
+        // located successfully from that module's ClassPathEntry list.
+        // There will not be another valid entry for that module.
+        return NULL;
+      }
+    }
+  }
+
+  return NULL;
+}
+
 instanceKlassHandle ClassLoader::load_class(Symbol* name, bool search_append_only, TRAPS) {
   assert(name != NULL, "invariant");
   assert(THREAD->is_Java_thread(), "must be a JavaThread");
@@ -1321,18 +1418,19 @@
   s2 classpath_index = 0;
   ClassPathEntry* e = NULL;
 
-  // If DumpSharedSpaces is true, boot loader visibility boundaries are set
-  // to be _first_entry to the end (all path entries). No -Xpatch entries are
-  // included since CDS and AppCDS are not supported if -Xpatch is specified.
+  // If DumpSharedSpaces is true boot loader visibility boundaries are set to:
+  //   - [jimage] + [_first_append_entry to _last_append_entry] (all path entries).
+  // No -Xpatch entries or exploded module builds are included since CDS
+  // is not supported if -Xpatch or exploded module builds are used.
   //
   // If search_append_only is true, boot loader visibility boundaries are
   // set to be _first_append_entry to the end. This includes:
   //   [-Xbootclasspath/a]; [jvmti appended entries]
   //
   // If both DumpSharedSpaces and search_append_only are false, boot loader
-  // visibility boundaries are set to be _first_entry to the entry before
-  // the _first_append_entry.  This would include:
-  //   [-Xpatch:<module>=<file>(<pathsep><file>)*];  [exploded build | jimage]
+  // visibility boundaries are set to be the -Xpatch entries plus the base piece.
+  // This would include:
+  //   [-Xpatch:<module>=<file>(<pathsep><file>)*]; [jimage | exploded module build]
   //
   // DumpSharedSpaces and search_append_only are mutually exclusive and cannot
   // be true at the same time.
@@ -1341,85 +1439,37 @@
   // Load Attempt #1: -Xpatch
   // Determine the class' defining module.  If it appears in the _xpatch_entries,
   // attempt to load the class from those locations specific to the module.
+  // Specifications to -Xpatch can contain a partial number of classes
+  // that are part of the overall module definition.  So if a particular class is not
+  // found within its module specification, the search should continue to Load Attempt #2.
   // Note: The -Xpatch entries are never searched if the boot loader's
   //       visibility boundary is limited to only searching the append entries.
   if (_xpatch_entries != NULL && !search_append_only && !DumpSharedSpaces) {
-    // Find the module in the boot loader's module entry table
-    PackageEntry* pkg_entry = get_package_entry(class_name, ClassLoaderData::the_null_class_loader_data(), THREAD);
-    ModuleEntry* mod_entry = (pkg_entry != NULL) ? pkg_entry->module() : NULL;
-
-    // If the module system has not defined java.base yet, then
-    // classes loaded are assumed to be defined to java.base.
-    // When java.base is eventually defined by the module system,
-    // all packages of classes that have been previously loaded
-    // are verified in ModuleEntryTable::verify_javabase_packages().
-    if (!Universe::is_module_initialized() &&
-        !ModuleEntryTable::javabase_defined() &&
-        mod_entry == NULL) {
-      mod_entry = ModuleEntryTable::javabase_module();
-    }
-
-    // The module must be a named module
-    if (mod_entry != NULL && mod_entry->is_named()) {
-      int num_of_entries = _xpatch_entries->length();
-      const Symbol* class_module_name = mod_entry->name();
-
-      // Loop through all the xpatch entries looking for module
-      for (int i = 0; i < num_of_entries; i++) {
-        ModuleClassPathList* module_cpl = _xpatch_entries->at(i);
-        Symbol* module_cpl_name = module_cpl->module_name();
-
-        if (module_cpl_name->fast_compare(class_module_name) == 0) {
-          // Class' module has been located, attempt to load
-          // the class from the module's ClassPathEntry list.
-          e = module_cpl->module_first_entry();
-          while (e != NULL) {
-            stream = e->open_stream(file_name, CHECK_NULL);
-            // No context.check is required since both CDS
-            // and AppCDS are turned off if -Xpatch is specified.
-            if (NULL != stream) {
-              break;
-            }
-            e = e->next();
-          }
-          // If the module was located in the xpatch entries, break out
-          // even if the class was not located successfully from that module's
-          // ClassPathEntry list. There will not be another valid entry for
-          // that module in the _xpatch_entries array.
-          break;
-        }
-      }
-    }
+    stream = search_module_entries(_xpatch_entries, class_name, file_name, CHECK_NULL);
   }
 
-  // Load Attempt #2: [exploded build | jimage]
+  // Load Attempt #2: [jimage | exploded build]
   if (!search_append_only && (NULL == stream)) {
-    e = _first_entry;
-    while ((e != NULL) && (e != _first_append_entry)) {
-      stream = e->open_stream(file_name, CHECK_NULL);
+    if (has_jrt_entry()) {
+      e = _jrt_entry;
+      stream = _jrt_entry->open_stream(file_name, CHECK_NULL);
       if (!context.check(stream, classpath_index)) {
         return NULL;
       }
-      if (NULL != stream) {
-        break;
-      }
-      e = e->next();
-      ++classpath_index;
+    } else {
+      // Exploded build - attempt to locate class in its defining module's location.
+      assert(_exploded_entries != NULL, "No exploded build entries present");
+      stream = search_module_entries(_exploded_entries, class_name, file_name, CHECK_NULL);
     }
   }
 
   // Load Attempt #3: [-Xbootclasspath/a]; [jvmti appended entries]
   if ((search_append_only || DumpSharedSpaces) && (NULL == stream)) {
-    // For the boot loader append path search, must calculate
-    // the starting classpath_index prior to attempting to
-    // load the classfile.
-    if (search_append_only) {
-      ClassPathEntry *tmp_e = _first_entry;
-      while ((tmp_e != NULL) && (tmp_e != _first_append_entry)) {
-        tmp_e = tmp_e->next();
-        ++classpath_index;
-      }
-    }
+    // For the boot loader append path search, the starting classpath_index
+    // for the appended piece is always 1 to account for either the
+    // _jrt_entry or the _exploded_entries.
+    assert(classpath_index == 0, "The classpath_index has been incremented incorrectly");
+    classpath_index = 1;
 
     e = _first_append_entry;
     while (e != NULL) {
@@ -1597,16 +1647,25 @@
 }
 
 // Complete the ClassPathEntry setup for the boot loader
-void classLoader_init2() {
+void ClassLoader::classLoader_init2(TRAPS) {
+  // Create the moduleEntry for java.base
+  create_javabase();
+
   // Setup the list of module/path pairs for -Xpatch processing
   // This must be done after the SymbolTable is created in order
   // to use fast_compare on module names instead of a string compare.
   if (Arguments::get_xpatchprefix() != NULL) {
-    ClassLoader::setup_xpatch_entries();
+    setup_xpatch_entries();
   }
 
-  // Determine if this is an exploded build
-  ClassLoader::set_has_jimage();
+  // Setup the initial java.base/path pair for the exploded build entries.
+  // As more modules are defined during module system initialization, more
+  // entries will be added to the exploded build array.
+  if (!has_jrt_entry()) {
+    assert(!DumpSharedSpaces, "DumpSharedSpaces not supported with exploded module builds");
+    assert(!UseSharedSpaces, "UsedSharedSpaces not supported with exploded module builds");
+    add_to_exploded_build_list(vmSymbols::java_base(), CHECK);
+  }
 }
 
 
@@ -1654,26 +1713,6 @@
   }
 }
 
-void ClassLoader::set_has_jimage() {
-  // Determine if this is an exploded build. When looking for
-  // the jimage file, only search the piece of the boot
-  // loader's boot class path which contains [exploded build | jimage].
-  // Do not search the boot loader's xpatch entries or append path.
-  ClassPathEntry* e = _first_entry;
-  ClassPathEntry* last_e = _first_append_entry;
-  while ((e != NULL) && (e != last_e)) {
-    JImageFile *jimage = e->jimage();
-    if (jimage != NULL && e->is_jrt()) {
-      _has_jimage = true;
-#if INCLUDE_CDS
-      ClassLoader::initialize_module_loader_map(jimage);
-#endif
-      return;
-    }
-    e = e->next();
-  }
-}
-
 #ifndef PRODUCT
 
 // CompileTheWorld
@@ -1762,14 +1801,19 @@
   HandleMark hm(THREAD);
   ResourceMark rm(THREAD);
 
+  assert(has_jrt_entry(), "Compile The World not supported with exploded module build");
+
   // Find bootstrap loader
   Handle system_class_loader (THREAD, SystemDictionary::java_system_loader());
-  // Iterate over all bootstrap class path entries
-  ClassPathEntry* e = _first_entry;
   jlong start = os::javaTimeMillis();
+
+  // Compile the world for the modular java runtime image
+  _jrt_entry->compile_the_world(system_class_loader, CATCH);
+
+  // Iterate over all bootstrap class path appended entries
+  ClassPathEntry* e = _first_append_entry;
   while (e != NULL) {
-    // We stop at "modules" jimage, unless it is the first bootstrap path entry
-    if (e->is_jrt() && e != _first_entry) break;
+    assert(!e->is_jrt(), "A modular java runtime image is present on the list of appended entries");
     e->compile_the_world(system_class_loader, CATCH);
     e = e->next();
   }
--- a/hotspot/src/share/vm/classfile/classLoader.hpp	Tue Jul 26 11:04:20 2016 +0200
+++ b/hotspot/src/share/vm/classfile/classLoader.hpp	Tue Jul 26 10:29:27 2016 -0400
@@ -216,34 +216,35 @@
   //  1. the module/path pairs specified to -Xpatch
   //    -Xpatch:<module>=<file>(<pathsep><file>)*
   //  2. the base piece
-  //    [exploded build | jimage]
+  //    [jimage | build with exploded modules]
   //  3. boot loader append path
   //    [-Xbootclasspath/a]; [jvmti appended entries]
   //
   // The boot loader must obey this order when attempting
   // to load a class.
 
-  // Contains the module/path pairs specified to -Xpatch
+  // 1. Contains the module/path pairs specified to -Xpatch
   static GrowableArray<ModuleClassPathList*>* _xpatch_entries;
 
-  // Contains the ClassPathEntry instances that include
-  // both the base piece and the boot loader append path.
-  static ClassPathEntry* _first_entry;
-  // Last entry in linked list of ClassPathEntry instances
-  static ClassPathEntry* _last_entry;
+  // 2. the base piece
+  //    Contains the ClassPathEntry of the modular java runtime image.
+  //    If no java runtime image is present, this indicates a
+  //    build with exploded modules is being used instead.
+  static ClassPathEntry* _jrt_entry;
+  static GrowableArray<ModuleClassPathList*>* _exploded_entries;
+  enum { EXPLODED_ENTRY_SIZE = 80 }; // Initial number of exploded modules
+
+  // 3. the boot loader's append path
+  //    [-Xbootclasspath/a]; [jvmti appended entries]
+  //    Note: boot loader append path does not support named modules.
+  static ClassPathEntry* _first_append_entry;
+  // Last entry in linked list of appended ClassPathEntry instances
+  static ClassPathEntry* _last_append_entry;
+
+  // Note: _num_entries includes the java runtime image and all
+  //       the entries on the _first_append_entry linked list.
   static int _num_entries;
 
-  // Marks the start of:
-  //   - the boot loader's append path
-  //     [-Xbootclasspath/a]; [jvmti appended entries]
-  // within the linked list of ClassPathEntry instances.
-  static ClassPathEntry* _first_append_entry;
-
-  static const char* _shared_archive;
-
-  // True if the boot path has a "modules" jimage
-  static bool _has_jimage;
-
   // Array of module names associated with the boot class loader
   CDS_ONLY(static GrowableArray<char*>* _boot_modules_array;)
 
@@ -253,9 +254,14 @@
   // Info used by CDS
   CDS_ONLY(static SharedPathsMiscInfo * _shared_paths_misc_info;)
 
-  // Initialization
+  // Initialization:
+  //   - setup the boot loader's system class path
+  //   - setup the boot loader's xpatch entries, if present
+  //   - create the ModuleEntry for java.base
   static void setup_bootstrap_search_path();
   static void setup_search_path(const char *class_path, bool setting_bootstrap);
+  static void setup_xpatch_entries();
+  static void create_javabase();
 
   static void load_zip_library();
   static void load_jimage_library();
@@ -285,8 +291,6 @@
   static int crc32(int crc, const char* buf, int len);
   static bool update_class_path_entry_list(const char *path,
                                            bool check_for_duplicates,
-                                           bool mark_append_entry,
-                                           bool prepend_entry,
                                            bool is_boot_append,
                                            bool throw_exception=true);
   static void print_bootclasspath();
@@ -352,15 +356,17 @@
     return _load_instance_class_failCounter;
   }
 
-  // Set up the module/path pairs as specified to -Xpatch
-  static void setup_xpatch_entries();
+  // Modular java runtime image is present vs. a build with exploded modules
+  static bool has_jrt_entry() { return (_jrt_entry != NULL); }
+  static ClassPathEntry* get_jrt_entry() { return _jrt_entry; }
 
-  // Sets _has_jimage to TRUE if "modules" jimage file exists
-  static void set_has_jimage();
-  static bool has_jimage() { return _has_jimage; }
+  // Add a module's exploded directory to the boot loader's exploded module build list
+  static void add_to_exploded_build_list(Symbol* module_name, TRAPS);
 
-  // Create the ModuleEntry for java.base
-  static void create_javabase();
+  // Attempt load of individual class from either the xpatch or exploded modules build lists
+  static ClassFileStream* search_module_entries(const GrowableArray<ModuleClassPathList*>* const module_list,
+                                                const char* const class_name,
+                                                const char* const file_name, TRAPS);
 
   // Load individual .class file
   static instanceKlassHandle load_class(Symbol* class_name, bool search_append_only, TRAPS);
@@ -381,17 +387,28 @@
 
   // Initialization
   static void initialize();
+  static void classLoader_init2(TRAPS);
   CDS_ONLY(static void initialize_shared_path();)
 
   static int compute_Object_vtable();
 
   static ClassPathEntry* classpath_entry(int n) {
-    ClassPathEntry* e = ClassLoader::_first_entry;
-    while (--n >= 0) {
-      assert(e != NULL, "Not that many classpath entries.");
-      e = e->next();
+    if (n == 0) {
+      assert(has_jrt_entry(), "No class path entry at 0 for exploded module builds");
+      return ClassLoader::_jrt_entry;
+    } else {
+      // The java runtime image is always the first entry
+      // in the FileMapInfo::_classpath_entry_table. Even though
+      // the _jrt_entry is not included in the _first_append_entry
+      // linked list, it must be accounted for when comparing the
+      // class path vs. the shared archive class path.
+      ClassPathEntry* e = ClassLoader::_first_append_entry;
+      while (--n >= 1) {
+        assert(e != NULL, "Not that many classpath entries.");
+        e = e->next();
+      }
+      return e;
     }
-    return e;
   }
 
 #if INCLUDE_CDS
@@ -429,18 +446,12 @@
   // adds a class path list
   static void add_to_list(ClassPathEntry* new_entry);
 
-  // prepends a class path list
-  static void prepend_to_list(ClassPathEntry* new_entry);
-
   // creates a class path zip entry (returns NULL if JAR file cannot be opened)
   static ClassPathZipEntry* create_class_path_zip_entry(const char *apath, bool is_boot_append);
 
   // add a path to class path list
   static void add_to_list(const char* apath);
 
-  // prepend a path to class path list
-  static void prepend_to_list(const char* apath);
-
   static bool string_ends_with(const char* str, const char* str_to_find);
 
   // obtain package name from a fully qualified class name
--- a/hotspot/src/share/vm/classfile/classLoaderExt.hpp	Tue Jul 26 11:04:20 2016 +0200
+++ b/hotspot/src/share/vm/classfile/classLoaderExt.hpp	Tue Jul 26 10:29:27 2016 -0400
@@ -71,22 +71,11 @@
 
 
   static void add_class_path_entry(const char* path, bool check_for_duplicates,
-                                   ClassPathEntry* new_entry, bool prepend_entry) {
-    if (prepend_entry) {
-      ClassLoader::prepend_to_list(new_entry);
-    } else {
-      ClassLoader::add_to_list(new_entry);
-    }
+                                   ClassPathEntry* new_entry) {
+    ClassLoader::add_to_list(new_entry);
   }
   static void append_boot_classpath(ClassPathEntry* new_entry) {
     ClassLoader::add_to_list(new_entry);
-    // During jvmti live phase an entry can be appended to the boot
-    // loader's ClassPathEntry instances.  Need to mark the start
-    // of the boot loader's append path in case there was no reason
-    // to mark it initially in setup_bootstrap_search_path.
-    if (ClassLoader::_first_append_entry == NULL) {
-      ClassLoader::set_first_append_entry(new_entry);
-    }
   }
   static void setup_search_paths() {}
   static bool is_boot_classpath(int classpath_index) {
--- a/hotspot/src/share/vm/classfile/modules.cpp	Tue Jul 26 11:04:20 2016 +0200
+++ b/hotspot/src/share/vm/classfile/modules.cpp	Tue Jul 26 10:29:27 2016 -0400
@@ -133,36 +133,6 @@
   return NULL;
 }
 
-// If using exploded build, append <java.home>/modules/module_name, if it exists,
-// to the system boot class path in order for the boot loader to locate class files.
-static void add_to_exploded_build_list(char *module_name, TRAPS) {
-  assert(!ClassLoader::has_jimage(), "Exploded build not applicable");
-  // java.base is handled by os::set_boot_path
-  assert(strcmp(module_name, "java.base") != 0, "Unexpected java.base module name");
-
-  char file_sep = os::file_separator()[0];
-  size_t module_len = strlen(module_name);
-
-  const char* home = Arguments::get_java_home();
-  size_t len = strlen(home) + module_len + 32;
-  char* path = NEW_C_HEAP_ARRAY(char, len, mtModule);
-  jio_snprintf(path, len, "%s%cmodules%c%s", home, file_sep, file_sep, module_name);
-  struct stat st;
-  // See if exploded module path exists
-  if ((os::stat(path, &st) != 0)) {
-    FREE_C_HEAP_ARRAY(char, path);
-    path = NULL;
-  }
-
-  if (path != NULL) {
-    HandleMark hm;
-    Handle loader_lock = Handle(THREAD, SystemDictionary::system_loader_lock());
-    ObjectLocker ol(loader_lock, THREAD);
-    log_info(class, load)("opened: %s", path);
-    ClassLoader::add_to_list(path);
-  }
-}
-
 bool Modules::is_package_defined(Symbol* package, Handle h_loader, TRAPS) {
   PackageEntry* res = get_package_entry_by_name(package, h_loader, CHECK_false);
   return res != NULL;
@@ -470,8 +440,8 @@
   // used, prepend <java.home>/modules/modules_name, if it exists, to the system boot class path.
   if (loader == NULL &&
       !Universe::is_module_initialized() &&
-      !ClassLoader::has_jimage()) {
-    add_to_exploded_build_list(module_name, CHECK);
+      !ClassLoader::has_jrt_entry()) {
+    ClassLoader::add_to_exploded_build_list(module_symbol, CHECK);
   }
 }
 
--- a/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp	Tue Jul 26 11:04:20 2016 +0200
+++ b/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp	Tue Jul 26 10:29:27 2016 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,7 +29,7 @@
 #include "runtime/os.hpp"
 
 // During dumping time, when processing class paths, we build up the dump-time
-// classpath. The JAR files that exist are stored in the list ClassLoader::_first_entry.
+// classpath. The JAR files that exist are stored in the list ClassLoader::_first_append_entry.
 // However, we need to store other "misc" information for run-time checking, such as
 //
 // + The values of Arguments::get_sysclasspath() used during dumping.
--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp	Tue Jul 26 11:04:20 2016 +0200
+++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp	Tue Jul 26 10:29:27 2016 -0400
@@ -2149,7 +2149,7 @@
 
   // Create the ModuleEntry for java.base.  This call needs to be done here,
   // after vmSymbols::initialize() is called but before any classes are pre-loaded.
-  ClassLoader::create_javabase();
+  ClassLoader::classLoader_init2(CHECK);
 
   // Preload commonly used klasses
   WKID scan = FIRST_WKID;
--- a/hotspot/src/share/vm/memory/filemap.cpp	Tue Jul 26 11:04:20 2016 +0200
+++ b/hotspot/src/share/vm/memory/filemap.cpp	Tue Jul 26 10:29:27 2016 -0400
@@ -199,11 +199,45 @@
   size_t entry_size = SharedClassUtil::shared_class_path_entry_size();
 
   for (int pass=0; pass<2; pass++) {
-    ClassPathEntry *cpe = ClassLoader::classpath_entry(0);
 
-    for (int cur_entry = 0 ; cpe != NULL; cpe = cpe->next(), cur_entry++) {
+    // Process the modular java runtime image first
+    ClassPathEntry* jrt_entry = ClassLoader::get_jrt_entry();
+    assert(jrt_entry != NULL,
+           "No modular java runtime image present when allocating the CDS classpath entry table");
+    const char *name = jrt_entry->name();
+    int name_bytes = (int)(strlen(name) + 1);
+    if (pass == 0) {
+      count++;
+      bytes += (int)entry_size;
+      bytes += name_bytes;
+      log_info(class, path)("add main shared path for modular java runtime image %s", name);
+    } else {
+      // The java runtime image is always in slot 0 on the shared class path.
+      SharedClassPathEntry* ent = shared_classpath(0);
+      struct stat st;
+      if (os::stat(name, &st) == 0) {
+        ent->_timestamp = st.st_mtime;
+        ent->_filesize = st.st_size;
+      }
+      if (ent->_filesize == 0) {
+        // unknown
+        ent->_filesize = -2;
+      }
+      ent->_name = strptr;
+      assert(strptr + name_bytes <= strptr_max, "miscalculated buffer size");
+      strncpy(strptr, name, (size_t)name_bytes); // name_bytes includes trailing 0.
+      strptr += name_bytes;
+    }
+
+    // Walk the appended entries, which includes the entries added for the classpath.
+    ClassPathEntry *cpe = ClassLoader::classpath_entry(1);
+
+    // Since the java runtime image is always in slot 0 on the shared class path, the
+    // appended entries are started at slot 1 immediately after.
+    for (int cur_entry = 1 ; cpe != NULL; cpe = cpe->next(), cur_entry++) {
       const char *name = cpe->name();
       int name_bytes = (int)(strlen(name) + 1);
+      assert(!cpe->is_jrt(), "A modular java runtime image is present on the list of appended entries");
 
       if (pass == 0) {
         count ++;
@@ -228,11 +262,7 @@
         } else {
           struct stat st;
           if (os::stat(name, &st) == 0) {
-            if (cpe->is_jrt()) {
-              // it's the "modules" jimage
-              ent->_timestamp = st.st_mtime;
-              ent->_filesize = st.st_size;
-            } else if ((st.st_mode & S_IFDIR) == S_IFDIR) {
+            if ((st.st_mode & S_IFDIR) == S_IFDIR) {
               if (!os::dir_is_empty(name)) {
                 ClassLoader::exit_with_path_failure(
                   "Cannot have non-empty directory in archived classpaths", name);
@@ -886,6 +916,11 @@
     return false;
   }
 
+  if (!Arguments::has_jimage()) {
+    FileMapInfo::fail_continue("The shared archive file cannot be used with an exploded module build.");
+    return false;
+  }
+
   if (_version != current_version()) {
     FileMapInfo::fail_continue("The shared archive file is the wrong version.");
     return false;
--- a/hotspot/src/share/vm/runtime/arguments.cpp	Tue Jul 26 11:04:20 2016 +0200
+++ b/hotspot/src/share/vm/runtime/arguments.cpp	Tue Jul 26 10:29:27 2016 -0400
@@ -85,7 +85,6 @@
 const char*  Arguments::_sun_java_launcher      = DEFAULT_JAVA_LAUNCHER;
 int    Arguments::_sun_java_launcher_pid        = -1;
 bool   Arguments::_sun_java_launcher_is_altjvm  = false;
-int    Arguments::_bootclassloader_append_index = -1;
 
 // These parameters are reset in method parse_vm_init_args()
 bool   Arguments::_AlwaysCompileLoopMethods     = AlwaysCompileLoopMethods;
@@ -113,6 +112,7 @@
 
 GrowableArray<ModuleXPatchPath*> *Arguments::_xpatchprefix = NULL;
 PathString *Arguments::_system_boot_class_path = NULL;
+bool Arguments::_has_jimage = false;
 
 char* Arguments::_ext_dirs = NULL;
 
@@ -1305,6 +1305,11 @@
     }
     sp = sp->next();
   }
+
+  // Check for an exploded module build in use with -Xshare:dump.
+  if (!has_jimage()) {
+    vm_exit_during_initialization("Dumping the shared archive is not supported with an exploded module build");
+  }
 }
 #endif
 
@@ -2676,7 +2681,6 @@
         return JNI_EINVAL;
     // -bootclasspath/a:
     } else if (match_option(option, "-Xbootclasspath/a:", &tail)) {
-      Arguments::set_bootclassloader_append_index((int)strlen(Arguments::get_sysclasspath())+1);
       Arguments::append_sysclasspath(tail);
     // -bootclasspath/p:
     } else if (match_option(option, "-Xbootclasspath/p:", &tail)) {
@@ -3323,18 +3327,6 @@
   _xpatchprefix->push(new ModuleXPatchPath(module_name, path));
 }
 
-// Set property jdk.boot.class.path.append to the contents of the bootclasspath
-// that follows either the jimage file or exploded module directories.  The
-// property will contain -Xbootclasspath/a and/or jvmti appended additions.
-void Arguments::set_jdkbootclasspath_append() {
-  char *sysclasspath = get_sysclasspath();
-  assert(sysclasspath != NULL, "NULL sysclasspath");
-  int bcp_a_idx = bootclassloader_append_index();
-  if (bcp_a_idx != -1 && bcp_a_idx < (int)strlen(sysclasspath)) {
-    _jdk_boot_class_path_append->set_value(sysclasspath + bcp_a_idx);
-  }
-}
-
 // Remove all empty paths from the app classpath (if IgnoreEmptyClassPaths is enabled)
 //
 // This is necessary because some apps like to specify classpath like -cp foo.jar:${XYZ}:bar.jar
@@ -3457,8 +3449,6 @@
     return JNI_ERR;
   }
 
-  Arguments::set_bootclassloader_append_index(((int)strlen(Arguments::get_sysclasspath()))+1);
-
   // This must be done after all arguments have been processed.
   // java_compiler() true means set to "NONE" or empty.
   if (java_compiler() && !xdebug_mode()) {
--- a/hotspot/src/share/vm/runtime/arguments.hpp	Tue Jul 26 11:04:20 2016 +0200
+++ b/hotspot/src/share/vm/runtime/arguments.hpp	Tue Jul 26 10:29:27 2016 -0400
@@ -363,6 +363,9 @@
   // -Xbootclasspath/p was supported.
   static PathString *_system_boot_class_path;
 
+  // Set if a modular java runtime image is present vs. a build with exploded modules
+  static bool _has_jimage;
+
   // temporary: to emit warning if the default ext dirs are not empty.
   // remove this variable when the warning is no longer needed.
   static char* _ext_dirs;
@@ -411,11 +414,6 @@
   static void set_java_compiler(bool arg) { _java_compiler = arg; }
   static bool java_compiler()   { return _java_compiler; }
 
-  // Capture the index location of -Xbootclasspath\a within sysclasspath.
-  // Used when setting up the bootstrap search path in order to
-  // mark the boot loader's append path observability boundary.
-  static int _bootclassloader_append_index;
-
   // -Xdebug flag
   static bool _xdebug_mode;
   static void set_xdebug_mode(bool arg) { _xdebug_mode = arg; }
@@ -669,17 +667,6 @@
   static size_t min_heap_size()             { return _min_heap_size; }
   static void  set_min_heap_size(size_t v)  { _min_heap_size = v;  }
 
-  // -Xbootclasspath/a
-  static int  bootclassloader_append_index() {
-    return _bootclassloader_append_index;
-  }
-  static void set_bootclassloader_append_index(int value) {
-    // Set only if the index has not been set yet
-    if (_bootclassloader_append_index == -1) {
-      _bootclassloader_append_index = value;
-    }
-  }
-
   // -Xrun
   static AgentLibrary* libraries()          { return _libraryList.first(); }
   static bool init_libraries_at_startup()   { return !_libraryList.is_empty(); }
@@ -739,19 +726,21 @@
 
   // Set up the underlying pieces of the system boot class path
   static void add_xpatchprefix(const char *module_name, const char *path, bool* xpatch_javabase);
-  static void set_sysclasspath(const char *value) {
+  static void set_sysclasspath(const char *value, bool has_jimage) {
+    // During start up, set by os::set_boot_path()
+    assert(get_sysclasspath() == NULL, "System boot class path previously set");
     _system_boot_class_path->set_value(value);
-    set_jdkbootclasspath_append();
+    _has_jimage = has_jimage;
   }
   static void append_sysclasspath(const char *value) {
     _system_boot_class_path->append_value(value);
-    set_jdkbootclasspath_append();
+    _jdk_boot_class_path_append->append_value(value);
   }
-  static void set_jdkbootclasspath_append();
 
   static GrowableArray<ModuleXPatchPath*>* get_xpatchprefix() { return _xpatchprefix; }
   static char* get_sysclasspath() { return _system_boot_class_path->value(); }
   static char* get_jdk_boot_class_path_append() { return _jdk_boot_class_path_append->value(); }
+  static bool has_jimage() { return _has_jimage; }
 
   static char* get_java_home()    { return _java_home->value(); }
   static char* get_dll_dir()      { return _sun_boot_library_path->value(); }
--- a/hotspot/src/share/vm/runtime/init.cpp	Tue Jul 26 11:04:20 2016 +0200
+++ b/hotspot/src/share/vm/runtime/init.cpp	Tue Jul 26 10:29:27 2016 -0400
@@ -53,7 +53,6 @@
 void management_init();
 void bytecodes_init();
 void classLoader_init1();
-void classLoader_init2(); // note: ClassLoader need 2-phase init
 void compilationPolicy_init();
 void codeCache_init();
 void VM_Version_init();
@@ -117,7 +116,6 @@
   if (status != JNI_OK)
     return status;
 
-  classLoader_init2();  // after SymbolTable creation, set up -Xpatch entries
   CodeCacheExtensions::complete_step(CodeCacheExtensionsSteps::Universe);
   interpreter_init();  // before any methods loaded
   CodeCacheExtensions::complete_step(CodeCacheExtensionsSteps::Interpreter);
--- a/hotspot/src/share/vm/runtime/os.cpp	Tue Jul 26 11:04:20 2016 +0200
+++ b/hotspot/src/share/vm/runtime/os.cpp	Tue Jul 26 10:29:27 2016 -0400
@@ -1213,7 +1213,7 @@
   if (jimage == NULL) return false;
   bool has_jimage = (os::stat(jimage, &st) == 0);
   if (has_jimage) {
-    Arguments::set_sysclasspath(jimage);
+    Arguments::set_sysclasspath(jimage, true);
     FREE_C_HEAP_ARRAY(char, jimage);
     return true;
   }
@@ -1223,7 +1223,7 @@
   char* base_classes = format_boot_path("%/modules/java.base", home, home_len, fileSep, pathSep);
   if (base_classes == NULL) return false;
   if (os::stat(base_classes, &st) == 0) {
-    Arguments::set_sysclasspath(base_classes);
+    Arguments::set_sysclasspath(base_classes, false);
     FREE_C_HEAP_ARRAY(char, base_classes);
     return true;
   }