# HG changeset patch # User lfoltan # Date 1469543367 14400 # Node ID 943cf01a6b820e4f5ee5cf869a1cccb8d83a943f # Parent f69cfe79fe98a5eb01d579e54463375a06f6ae7a 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 diff -r f69cfe79fe98 -r 943cf01a6b82 hotspot/src/share/vm/classfile/classLoader.cpp --- 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* ClassLoader::_xpatch_entries = NULL; -ClassPathEntry* ClassLoader::_first_entry = NULL; -ClassPathEntry* ClassLoader::_last_entry = NULL; +GrowableArray* 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* ClassLoader::_boot_modules_array = NULL; GrowableArray* 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* 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(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); + + // 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 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; + // 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(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* 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* 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:=()*]; [exploded build | jimage] + // visibility boundaries are set to be the -Xpatch entries plus the base piece. + // This would include: + // [-Xpatch:=()*]; [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(); } diff -r f69cfe79fe98 -r 943cf01a6b82 hotspot/src/share/vm/classfile/classLoader.hpp --- 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,33 +216,34 @@ // 1. the module/path pairs specified to -Xpatch // -Xpatch:=()* // 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* _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; - static int _num_entries; + // 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* _exploded_entries; + enum { EXPLODED_ENTRY_SIZE = 80 }; // Initial number of exploded modules - // Marks the start of: - // - the boot loader's append path - // [-Xbootclasspath/a]; [jvmti appended entries] - // within the linked list of ClassPathEntry instances. + // 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; - - static const char* _shared_archive; + // Last entry in linked list of appended ClassPathEntry instances + static ClassPathEntry* _last_append_entry; - // True if the boot path has a "modules" jimage - static bool _has_jimage; + // Note: _num_entries includes the java runtime image and all + // the entries on the _first_append_entry linked list. + static int _num_entries; // Array of module names associated with the boot class loader CDS_ONLY(static GrowableArray* _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* 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 diff -r f69cfe79fe98 -r 943cf01a6b82 hotspot/src/share/vm/classfile/classLoaderExt.hpp --- 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) { diff -r f69cfe79fe98 -r 943cf01a6b82 hotspot/src/share/vm/classfile/modules.cpp --- 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 /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 /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); } } diff -r f69cfe79fe98 -r 943cf01a6b82 hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp --- 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. diff -r f69cfe79fe98 -r 943cf01a6b82 hotspot/src/share/vm/classfile/systemDictionary.cpp --- 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; diff -r f69cfe79fe98 -r 943cf01a6b82 hotspot/src/share/vm/memory/filemap.cpp --- 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; diff -r f69cfe79fe98 -r 943cf01a6b82 hotspot/src/share/vm/runtime/arguments.cpp --- 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 *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()) { diff -r f69cfe79fe98 -r 943cf01a6b82 hotspot/src/share/vm/runtime/arguments.hpp --- 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* 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(); } diff -r f69cfe79fe98 -r 943cf01a6b82 hotspot/src/share/vm/runtime/init.cpp --- 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); diff -r f69cfe79fe98 -r 943cf01a6b82 hotspot/src/share/vm/runtime/os.cpp --- 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; }