< prev index next > src/hotspot/share/classfile/classLoader.cpp
Print this page
static JImageOpen_t JImageOpen = nullptr;
static JImageClose_t JImageClose = nullptr;
static JImageFindResource_t JImageFindResource = nullptr;
static JImageGetResource_t JImageGetResource = nullptr;
- // JimageFile pointer, or null if exploded JDK build.
+ // JImageFile pointer, or null if exploded JDK build.
static JImageFile* JImage_file = nullptr;
+ // JImageMode status to control preview behaviour. JImage_file is unusable
+ // for normal lookup until (JImage_mode != JIMAGE_MODE_UNINITIALIZED).
+ enum JImageMode {
+ JIMAGE_MODE_UNINITIALIZED = 0,
+ JIMAGE_MODE_DEFAULT = 1,
+ JIMAGE_MODE_ENABLE_PREVIEW = 2
+ };
+ static JImageMode JImage_mode = JIMAGE_MODE_UNINITIALIZED;
+
// Globals
PerfCounter* ClassLoader::_perf_accumulated_time = nullptr;
PerfCounter* ClassLoader::_perf_classes_inited = nullptr;
PerfCounter* ClassLoader::_perf_class_init_time = nullptr;
st->cr();
}
GrowableArray<ModuleClassPathList*>* ClassLoader::_patch_mod_entries = nullptr;
GrowableArray<ModuleClassPathList*>* ClassLoader::_exploded_entries = nullptr;
- ClassPathEntry* ClassLoader::_jrt_entry = nullptr;
+ ClassPathImageEntry* ClassLoader::_jrt_entry = nullptr;
ClassPathEntry* volatile ClassLoader::_first_append_entry_list = nullptr;
ClassPathEntry* volatile ClassLoader::_last_append_entry = nullptr;
// helper routines
}
return (strncmp(str, str_to_find, str_to_find_len) == 0);
}
#endif
- static const char* get_jimage_version_string() {
- static char version_string[10] = "";
- if (version_string[0] == '\0') {
- jio_snprintf(version_string, sizeof(version_string), "%d.%d",
- VM_Version::vm_major_version(), VM_Version::vm_minor_version());
- }
- return (const char*)version_string;
- }
-
bool ClassLoader::string_ends_with(const char* str, const char* str_to_find) {
size_t str_len = strlen(str);
size_t str_to_find_len = strlen(str_to_find);
if (str_to_find_len > str_len) {
return false;
return nullptr;
}
return SymbolTable::new_symbol(name, pointer_delta_as_int(start, base), pointer_delta_as_int(end, base));
}
+ // --------------------------------
+ // The following jimage_xxx static functions encapsulate all JImage_file and JImage_mode access.
+ // This is done to make it easy to reason about the JImage file state (exists vs initialized etc.).
+
+ // Opens the named JImage file and sets the JImage file reference.
+ // Returns true if opening the JImage file was successful (see also jimage_exists()).
+ static bool jimage_open(const char* modules_path) {
+ // Currently 'error' is not set to anything useful, so ignore it here.
+ jint error;
+ JImage_file = (*JImageOpen)(modules_path, &error);
+ return JImage_file != nullptr;
+ }
+
+ // Closes and clears the JImage file reference (this will only be called during shutdown).
+ static void jimage_close() {
+ if (JImage_file != nullptr) {
+ (*JImageClose)(JImage_file);
+ JImage_file = nullptr;
+ }
+ }
+
+ // Returns whether a JImage file was opened (but NOT whether it was initialized yet).
+ static bool jimage_exists() {
+ return JImage_file != nullptr;
+ }
+
+ // Returns the JImage file reference (which may or may not be initialized).
+ static JImageFile* jimage_non_null() {
+ assert(jimage_exists(), "should have been opened by ClassLoader::lookup_vm_options "
+ "and remained throughout normal JVM lifetime");
+ return JImage_file;
+ }
+
+ // Called once to set the access mode for resource (i.e. preview or non-preview) before
+ // general resource lookup can occur.
+ static void jimage_init(bool enable_preview) {
+ assert(JImage_mode == JIMAGE_MODE_UNINITIALIZED, "jimage_init must not be called twice");
+ JImage_mode = enable_preview ? JIMAGE_MODE_ENABLE_PREVIEW : JIMAGE_MODE_DEFAULT;
+ }
+
+ // Returns true if jimage_init() has been called. Once the JImage file is initialized,
+ // jimage_is_preview_enabled() can be called to correctly determine the access mode.
+ static bool jimage_is_initialized() {
+ return jimage_exists() && JImage_mode != JIMAGE_MODE_UNINITIALIZED;
+ }
+
+ // Returns the access mode for an initialized JImage file (reflects --enable-preview).
+ static bool jimage_is_preview_enabled() {
+ assert(jimage_is_initialized(), "jimage is not initialized");
+ return JImage_mode == JIMAGE_MODE_ENABLE_PREVIEW;
+ }
+
+ // Looks up the location of a named JImage resource. This "raw" lookup function allows
+ // the preview mode to be manually specified, so must not be accessible outside this
+ // class. ClassPathImageEntry manages all calls for resources after startup is complete.
+ static JImageLocationRef jimage_find_resource(const char* module_name,
+ const char* file_name,
+ bool is_preview,
+ jlong *size) {
+ return ((*JImageFindResource)(jimage_non_null(),
+ module_name,
+ file_name,
+ is_preview,
+ size));
+ }
+ // --------------------------------
+
// Given a fully qualified package name, find its defining package in the class loader's
// package entry table.
PackageEntry* ClassLoader::get_package_entry(Symbol* pkg_name, ClassLoaderData* loader_data) {
if (pkg_name == nullptr) {
return nullptr;
_zip_name);
}
DEBUG_ONLY(ClassPathImageEntry* ClassPathImageEntry::_singleton = nullptr;)
- JImageFile* ClassPathImageEntry::jimage() const {
- return JImage_file;
- }
-
- JImageFile* ClassPathImageEntry::jimage_non_null() const {
- assert(ClassLoader::has_jrt_entry(), "must be");
- assert(jimage() != nullptr, "should have been opened by ClassLoader::lookup_vm_options "
- "and remained throughout normal JVM lifetime");
- return jimage();
- }
-
void ClassPathImageEntry::close_jimage() {
- if (jimage() != nullptr) {
- (*JImageClose)(jimage());
- JImage_file = nullptr;
- }
+ jimage_close();
}
- ClassPathImageEntry::ClassPathImageEntry(JImageFile* jimage, const char* name) :
+ ClassPathImageEntry::ClassPathImageEntry(const char* name) :
ClassPathEntry() {
- guarantee(jimage != nullptr, "jimage file is null");
+ guarantee(jimage_is_initialized(), "jimage is not initialized");
guarantee(name != nullptr, "jimage file name is null");
+
assert(_singleton == nullptr, "VM supports only one jimage");
DEBUG_ONLY(_singleton = this);
size_t len = strlen(name) + 1;
_name = copy_path(name);
}
// Assumptions:
// 1. There are no unnamed modules in the jimage file.
// 2. A package is in at most one module in the jimage file.
//
ClassFileStream* ClassPathImageEntry::open_stream_for_loader(JavaThread* current, const char* name, ClassLoaderData* loader_data) {
+ bool is_preview = jimage_is_preview_enabled();
+
jlong size;
- JImageLocationRef location = (*JImageFindResource)(jimage_non_null(), "", get_jimage_version_string(), name, &size);
+ JImageLocationRef location = jimage_find_resource("", name, is_preview, &size);
if (location == 0) {
TempNewSymbol class_name = SymbolTable::new_symbol(name);
TempNewSymbol pkg_name = ClassLoader::package_from_class_name(class_name);
if (pkg_name != nullptr) {
if (!Universe::is_module_initialized()) {
- location = (*JImageFindResource)(jimage_non_null(), JAVA_BASE_NAME, get_jimage_version_string(), name, &size);
+ location = jimage_find_resource(JAVA_BASE_NAME, name, is_preview, &size);
} else {
PackageEntry* package_entry = ClassLoader::get_package_entry(pkg_name, loader_data);
if (package_entry != nullptr) {
ResourceMark rm(current);
// Get the module name
ModuleEntry* module = package_entry->module();
assert(module != nullptr, "Boot classLoader package missing module");
assert(module->is_named(), "Boot classLoader package is in unnamed module");
const char* module_name = module->name()->as_C_string();
if (module_name != nullptr) {
- location = (*JImageFindResource)(jimage_non_null(), module_name, get_jimage_version_string(), name, &size);
+ location = jimage_find_resource(module_name, name, is_preview, &size);
}
}
}
}
}
ClassLoader::perf_sys_classfile_bytes_read()->inc(size);
}
char* data = NEW_RESOURCE_ARRAY(char, size);
(*JImageGetResource)(jimage_non_null(), location, data, size);
// Resource allocated
- assert(this == (ClassPathImageEntry*)ClassLoader::get_jrt_entry(), "must be");
+ assert(this == ClassLoader::get_jrt_entry(), "must be");
return new ClassFileStream((u1*)data,
checked_cast<int>(size),
_name,
true); // from_boot_loader_modules_image
}
return nullptr;
}
- JImageLocationRef ClassLoader::jimage_find_resource(JImageFile* jf,
- const char* module_name,
- const char* file_name,
- jlong &size) {
- return ((*JImageFindResource)(jf, module_name, get_jimage_version_string(), file_name, &size));
- }
-
bool ClassPathImageEntry::is_modules_image() const {
assert(this == _singleton, "VM supports a single jimage");
- assert(this == (ClassPathImageEntry*)ClassLoader::get_jrt_entry(), "must be used for jrt entry");
+ assert(this == ClassLoader::get_jrt_entry(), "must be used for jrt entry");
return true;
}
ModuleClassPathList::ModuleClassPathList(Symbol* module_name) {
_module_name = module_name;
assert(string_ends_with(path, MODULES_IMAGE_NAME) || string_ends_with(path, JAVA_BASE_NAME),
"Incorrect boot loader search path, no java runtime image or " JAVA_BASE_NAME " exploded build");
struct stat st;
if (os::stat(path, &st) == 0) {
// Directory found
- if (JImage_file != nullptr) {
+ if (jimage_exists()) {
assert(Arguments::has_jimage(), "sanity check");
const char* canonical_path = get_canonical_path(path, current);
assert(canonical_path != nullptr, "canonical_path issue");
- _jrt_entry = new ClassPathImageEntry(JImage_file, canonical_path);
+ // Hand over lifecycle control of the JImage file to the _jrt_entry singleton
+ // (see ClassPathImageEntry::close_jimage). The image must be initialized by now.
+ _jrt_entry = new ClassPathImageEntry(canonical_path);
assert(_jrt_entry != nullptr && _jrt_entry->is_modules_image(), "No java runtime image present");
- assert(_jrt_entry->jimage() != nullptr, "No java runtime image");
} // else it's an exploded build.
} else {
// If path does not exist, exit
vm_exit_during_initialization("Unable to establish the boot loader search path", path);
}
// Lookup stream for parsing .class file
ClassFileStream* stream = nullptr;
s2 classpath_index = 0;
ClassPathEntry* e = nullptr;
+ bool is_patched = false;
// 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]
//
// 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 --patch-module entries are never searched if the boot loader's
// visibility boundary is limited to only searching the append entries.
if (_patch_mod_entries != nullptr && !search_append_only) {
- assert(!CDSConfig::is_dumping_archive(), "CDS doesn't support --patch-module during dumping");
- stream = search_module_entries(THREAD, _patch_mod_entries, pkg_entry, file_name);
+ // At CDS dump time, the --patch-module entries are ignored. That means a
+ // class is still loaded from the runtime image even if it might
+ // appear in the _patch_mod_entries. The runtime shared class visibility
+ // check will determine if a shared class is visible based on the runtime
+ // environment, including the runtime --patch-module setting.
+ if (!CDSConfig::is_valhalla_preview()) {
+ // Dynamic dumping requires UseSharedSpaces to be enabled. Since --patch-module
+ // is not supported with UseSharedSpaces, we can never come here during dynamic dumping.
+ assert(!CDSConfig::is_dumping_archive(), "CDS doesn't support --patch-module during dumping");
+ }
+ if (CDSConfig::is_valhalla_preview() || !CDSConfig::is_dumping_static_archive()) {
+ stream = search_module_entries(THREAD, _patch_mod_entries, pkg_entry, file_name);
+ if (stream != nullptr) {
+ is_patched = true;
+ }
+ }
}
// Load Attempt #2: [jimage | exploded build]
if (!search_append_only && (nullptr == stream)) {
if (has_jrt_entry()) {
name,
loader_data,
cl_info,
CHECK_NULL);
result->set_classpath_index(classpath_index);
+ if (is_patched) {
+ result->set_shared_classpath_index(0);
+ }
return result;
}
#if INCLUDE_CDS
static const char* skip_uri_protocol(const char* source) {
if (ik->is_hidden()) {
record_hidden_class(ik);
return;
}
+ if (ik->shared_classpath_index() == 0 && ik->defined_by_boot_loader()) {
+ return;
+ }
+
oop loader = ik->class_loader();
char* src = (char*)stream->source();
if (src == nullptr) {
ik->set_shared_classpath_index(-1); // unsupported location
return;
load_java_library();
// jimage library entry points are loaded below, in lookup_vm_options
setup_bootstrap_search_path(THREAD);
}
- static char* lookup_vm_resource(JImageFile *jimage, const char *jimage_version, const char *path) {
- jlong size;
- JImageLocationRef location = (*JImageFindResource)(jimage, "java.base", jimage_version, path, &size);
- if (location == 0)
- return nullptr;
- char *val = NEW_C_HEAP_ARRAY(char, size+1, mtClass);
- (*JImageGetResource)(jimage, location, val, size);
- val[size] = '\0';
- return val;
- }
-
// Lookup VM options embedded in the modules jimage file
char* ClassLoader::lookup_vm_options() {
- jint error;
char modules_path[JVM_MAXPATHLEN];
const char* fileSep = os::file_separator();
// Initialize jimage library entry points
load_jimage_library();
jio_snprintf(modules_path, JVM_MAXPATHLEN, "%s%slib%smodules", Arguments::get_java_home(), fileSep, fileSep);
- JImage_file =(*JImageOpen)(modules_path, &error);
- if (JImage_file == nullptr) {
- return nullptr;
+ if (jimage_open(modules_path)) {
+ // Special case where we lookup the options string *before* calling jimage_init().
+ // Since VM arguments have not been parsed, and the ClassPathImageEntry singleton
+ // has not been created yet, we access the JImage file directly in non-preview mode.
+ jlong size;
+ JImageLocationRef location =
+ jimage_find_resource(JAVA_BASE_NAME, "jdk/internal/vm/options", /* is_preview */ false, &size);
+ if (location != 0) {
+ char *options = NEW_C_HEAP_ARRAY(char, size+1, mtClass);
+ (*JImageGetResource)(jimage_non_null(), location, options, size);
+ options[size] = '\0';
+ return options;
+ }
}
+ return nullptr;
+ }
- const char *jimage_version = get_jimage_version_string();
- char *options = lookup_vm_resource(JImage_file, jimage_version, "jdk/internal/vm/options");
- return options;
+ // Finishes initializing the JImageFile (if present) by setting the access mode.
+ void ClassLoader::init_jimage(bool enable_preview) {
+ if (jimage_exists()) {
+ jimage_init(enable_preview);
+ }
}
bool ClassLoader::is_module_observable(const char* module_name) {
assert(JImageOpen != nullptr, "jimage library should have been opened");
- if (JImage_file == nullptr) {
+ if (!jimage_exists()) {
struct stat st;
const char *path = get_exploded_module_path(module_name, true);
bool res = os::stat(path, &st) == 0;
FREE_C_HEAP_ARRAY(char, path);
return res;
}
+ // We don't expect preview mode (i.e. --enable-preview) to affect module visibility.
jlong size;
- const char *jimage_version = get_jimage_version_string();
- return (*JImageFindResource)(JImage_file, module_name, jimage_version, "module-info.class", &size) != 0;
+ return jimage_find_resource(module_name, "module-info.class", /* is_preview */ false, &size) != 0;
}
jlong ClassLoader::classloader_time_ms() {
return UsePerfData ?
Management::ticks_to_ms(_perf_accumulated_time->get_value()) : -1;
< prev index next >