diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index b38450358cd..2872b492631 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -96,9 +96,18 @@ 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; @@ -153,7 +162,7 @@ void ClassLoader::print_counters(outputStream *st) { GrowableArray* ClassLoader::_patch_mod_entries = nullptr; GrowableArray* 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; @@ -170,15 +179,6 @@ static bool string_starts_with(const char* str, const char* str_to_find) { } #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); @@ -233,6 +233,73 @@ Symbol* ClassLoader::package_from_class_name(const Symbol* name, bool* bad_class 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) { @@ -371,28 +438,15 @@ ClassFileStream* ClassPathZipEntry::open_stream(JavaThread* current, const char* 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; @@ -411,8 +465,10 @@ ClassFileStream* ClassPathImageEntry::open_stream(JavaThread* current, const cha // 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); @@ -420,7 +476,7 @@ ClassFileStream* ClassPathImageEntry::open_stream_for_loader(JavaThread* current 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) { @@ -431,7 +487,7 @@ ClassFileStream* ClassPathImageEntry::open_stream_for_loader(JavaThread* current 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); } } } @@ -444,7 +500,7 @@ ClassFileStream* ClassPathImageEntry::open_stream_for_loader(JavaThread* current 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(size), _name, @@ -454,16 +510,9 @@ ClassFileStream* ClassPathImageEntry::open_stream_for_loader(JavaThread* current 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; } @@ -618,14 +667,15 @@ void ClassLoader::setup_bootstrap_search_path_impl(JavaThread* current, const ch 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 @@ -1439,20 +1489,8 @@ void ClassLoader::initialize(TRAPS) { 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(); @@ -1460,28 +1498,42 @@ char* ClassLoader::lookup_vm_options() { 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() { diff --git a/src/hotspot/share/classfile/classLoader.hpp b/src/hotspot/share/classfile/classLoader.hpp index a946f1f4e25..574dd3ccd07 100644 --- a/src/hotspot/share/classfile/classLoader.hpp +++ b/src/hotspot/share/classfile/classLoader.hpp @@ -99,7 +99,8 @@ class ClassPathZipEntry: public ClassPathEntry { }; -// For java image files +// A singleton path entry which takes ownership of the initialized JImageFile +// reference. Not used for exploded builds. class ClassPathImageEntry: public ClassPathEntry { private: const char* _name; @@ -107,11 +108,12 @@ class ClassPathImageEntry: public ClassPathEntry { public: bool is_modules_image() const; const char* name() const { return _name == nullptr ? "" : _name; } - JImageFile* jimage() const; - JImageFile* jimage_non_null() const; + // Called to close the JImage during os::abort (normally not called). void close_jimage(); - ClassPathImageEntry(JImageFile* jimage, const char* name); + // Takes effective ownership of the static JImageFile pointer. + ClassPathImageEntry(const char* name); virtual ~ClassPathImageEntry() { ShouldNotReachHere(); } + ClassFileStream* open_stream(JavaThread* current, const char* name); ClassFileStream* open_stream_for_loader(JavaThread* current, const char* name, ClassLoaderData* loader_data); }; @@ -200,10 +202,10 @@ class ClassLoader: AllStatic { static GrowableArray* _patch_mod_entries; // 2. the base piece - // Contains the ClassPathEntry of the modular java runtime image. + // Contains the ClassPathImageEntry 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 ClassPathImageEntry* _jrt_entry; static GrowableArray* _exploded_entries; enum { EXPLODED_ENTRY_SIZE = 80 }; // Initial number of exploded modules @@ -350,15 +352,20 @@ class ClassLoader: AllStatic { static void append_boot_classpath(ClassPathEntry* new_entry); #endif + // Retrieves additional VM options prior to flags processing. Options held + // in the JImage file are retrieved without fully initializing it. (this is + // the only JImage lookup which can succeed before init_jimage() is called). static char* lookup_vm_options(); + // Called once, after all flags are processed, to finish initializing the + // JImage file. Until this is called, jimage_find_resource(), and any other + // JImage resource lookups or access will fail. + static void init_jimage(bool enable_preview); + // Determines if the named module is present in the // modules jimage file or in the exploded modules directory. static bool is_module_observable(const char* module_name); - static JImageLocationRef jimage_find_resource(JImageFile* jf, const char* module_name, - const char* file_name, jlong &size); - static void trace_class_path(const char* msg, const char* name = nullptr); // VM monitoring and management support diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index d311e172bcc..adb561ef41b 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -2079,6 +2079,18 @@ int Arguments::process_patch_mod_option(const char* patch_mod_tail) { return JNI_OK; } +// Temporary system property to disable preview patching and enable the new preview mode +// feature for testing/development. Once the preview mode feature is finished, the value +// will be always 'true' and this code, and all related dead-code can be removed. +#define DISABLE_PREVIEW_PATCHING_DEFAULT false + +bool Arguments::disable_preview_patching() { + const char* prop = get_property("DISABLE_PREVIEW_PATCHING"); + return (prop != nullptr) + ? strncmp(prop, "true", strlen("true")) == 0 + : DISABLE_PREVIEW_PATCHING_DEFAULT; +} + // VALUECLASS_STR must match string used in the build #define VALUECLASS_STR "valueclasses" #define VALUECLASS_JAR "-" VALUECLASS_STR ".jar" @@ -2086,11 +2098,17 @@ int Arguments::process_patch_mod_option(const char* patch_mod_tail) { // Finalize --patch-module args and --enable-preview related to value class module patches. // Create all numbered properties passing module patches. int Arguments::finalize_patch_module() { - // If --enable-preview and EnableValhalla is true, each module may have value classes that - // are to be patched into the module. + // If --enable-preview and EnableValhalla is true, modules may have preview mode resources. + bool enable_valhalla_preview = enable_preview() && EnableValhalla; + // Whether to use module patching, or the new preview mode feature for preview resources. + bool disable_patching = disable_preview_patching(); + + // This must be called, even with 'false', to enable resource lookup from JImage. + ClassLoader::init_jimage(disable_patching && enable_valhalla_preview); + // For each -valueclasses.jar in /lib/valueclasses/ // appends the equivalent of --patch-module =/lib/valueclasses/-valueclasses.jar - if (enable_preview() && EnableValhalla) { + if (!disable_patching && enable_valhalla_preview) { char * valueclasses_dir = AllocateHeap(JVM_MAXPATHLEN, mtArguments); const char * fileSep = os::file_separator(); @@ -2123,7 +2141,7 @@ int Arguments::finalize_patch_module() { } // Create numbered properties for each module that has been patched either - // by --patch-module or --enable-preview + // by --patch-module (or --enable-preview if disable_patching is false). // Format is "jdk.module.patch.==" if (_patch_mod_prefix != nullptr) { char * prop_value = AllocateHeap(JVM_MAXPATHLEN + JVM_MAXPATHLEN + 1, mtArguments); diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp index bc2de98a638..f5a1a102fcf 100644 --- a/src/hotspot/share/runtime/arguments.hpp +++ b/src/hotspot/share/runtime/arguments.hpp @@ -486,6 +486,8 @@ class Arguments : AllStatic { // Set up the underlying pieces of the boot class path static void add_patch_mod_prefix(const char *module_name, const char *path, bool allow_append, bool allow_cds); static int finalize_patch_module(); + static bool disable_preview_patching(); + static void set_boot_class_path(const char *value, bool has_jimage) { // During start up, set by os::set_boot_path() assert(get_boot_class_path() == nullptr, "Boot class path previously set"); diff --git a/src/java.base/share/native/libjimage/imageFile.cpp b/src/java.base/share/native/libjimage/imageFile.cpp index d97a8f95a60..60c2cf44e79 100644 --- a/src/java.base/share/native/libjimage/imageFile.cpp +++ b/src/java.base/share/native/libjimage/imageFile.cpp @@ -319,7 +319,12 @@ bool ImageFileReader::open() { !read_at((u1*)&_header, header_size, 0) || _header.magic(_endian) != IMAGE_MAGIC || _header.major_version(_endian) != MAJOR_VERSION || - _header.minor_version(_endian) != MINOR_VERSION) { + // Temporarily, we allow either version (1.1 or 1.0) of the file to + // be read so this code can be committed before image writing changes + // for preview mode. Preview mode changes do not modify any structure, + // so a 1.0 file will look like a jimage without any preview resources. + // TODO: Restore equality check for MINOR_VERSION. + _header.minor_version(_endian) > MINOR_VERSION) { close(); return false; } @@ -404,7 +409,7 @@ u4 ImageFileReader::find_location_index(const char* path, u8 *size) const { // Make sure result is not a false positive. if (verify_location(location, path)) { *size = (jlong)location.get_attribute(ImageLocation::ATTRIBUTE_UNCOMPRESSED); - return offset; + return offset; } } return 0; // not found @@ -435,7 +440,7 @@ bool ImageFileReader::verify_location(ImageLocation& location, const char* path) } // Get base name string. const char* base = location.get_attribute(ImageLocation::ATTRIBUTE_BASE, strings); - // Compare with basne name. + // Compare with base name. if (!(next = ImageStrings::starts_with(next, base))) return false; // Get extension string. const char* extension = location.get_attribute(ImageLocation::ATTRIBUTE_EXTENSION, strings); diff --git a/src/java.base/share/native/libjimage/imageFile.hpp b/src/java.base/share/native/libjimage/imageFile.hpp index 5fb4ea3baaa..ea0afa98380 100644 --- a/src/java.base/share/native/libjimage/imageFile.hpp +++ b/src/java.base/share/native/libjimage/imageFile.hpp @@ -237,13 +237,27 @@ class ImageLocation { ATTRIBUTE_MODULE, // String table offset of module name ATTRIBUTE_PARENT, // String table offset of resource path parent ATTRIBUTE_BASE, // String table offset of resource path base - ATTRIBUTE_EXTENSION, // String table offset of resource path extension + ATTRIBUTE_EXTENSION, // String table offset of resource path extension ATTRIBUTE_OFFSET, // Container byte offset of resource - ATTRIBUTE_COMPRESSED, // In image byte size of the compressed resource - ATTRIBUTE_UNCOMPRESSED, // In memory byte size of the uncompressed resource + ATTRIBUTE_COMPRESSED, // In-image byte size of the compressed resource + ATTRIBUTE_UNCOMPRESSED, // In-memory byte size of the uncompressed resource + ATTRIBUTE_PREVIEW_FLAGS, // Flags relating to preview mode resources. ATTRIBUTE_COUNT // Number of attribute kinds }; + // Flag masks for the ATTRIBUTE_PREVIEW_FLAGS attribute. Defined so + // that zero is the overwhelmingly common case for normal resources. + enum { + // Set on a "normal" (non-preview) location if a preview version of + // it exists in the same module. + FLAGS_HAS_PREVIEW_VERSION = 0x1, + // Set on all preview locations in "/modules/xxx/META-INF/preview/..." + FLAGS_IS_PREVIEW_VERSION = 0x2, + // Set on a preview location if no normal (non-preview) version of + // it exists in the same module. + FLAGS_IS_PREVIEW_ONLY = 0x4 + }; + private: // Values of inflated attributes. u8 _attributes[ATTRIBUTE_COUNT]; @@ -300,6 +314,11 @@ class ImageLocation { inline const char* get_attribute(u4 kind, const ImageStrings& strings) const { return strings.get((u4)get_attribute(kind)); } + + // Retrieve flags from the ATTRIBUTE_PREVIEW_FLAGS attribute. + inline u4 get_preview_flags() const { + return (u4) get_attribute(ATTRIBUTE_PREVIEW_FLAGS); + } }; // Image file header, starting at offset 0. @@ -394,6 +413,7 @@ class ImageFileReaderTable { // leads the ImageFileReader to be actually closed and discarded. class ImageFileReader { friend class ImageFileReaderTable; +friend class PackageFlags; private: // Manage a number of image files such that an image can be shared across // multiple uses (ex. loader.) @@ -433,7 +453,7 @@ friend class ImageFileReaderTable; // Image file major version number. MAJOR_VERSION = 1, // Image file minor version number. - MINOR_VERSION = 0 + MINOR_VERSION = 1 }; // Locate an image if file already open. diff --git a/src/java.base/share/native/libjimage/jimage.cpp b/src/java.base/share/native/libjimage/jimage.cpp index 10e85eb2520..b20e9b027b4 100644 --- a/src/java.base/share/native/libjimage/jimage.cpp +++ b/src/java.base/share/native/libjimage/jimage.cpp @@ -91,45 +91,97 @@ JIMAGE_Close(JImageFile* image) { * name, a version string and the name of a class/resource, return location * information describing the resource and its size. If no resource is found, the * function returns JIMAGE_NOT_FOUND and the value of size is undefined. - * The version number should be "9.0" and is not used in locating the resource. * The resulting location does/should not have to be released. * All strings are utf-8, zero byte terminated. * * Ex. * jlong size; * JImageLocationRef location = (*JImageFindResource)(image, - * "java.base", "9.0", "java/lang/String.class", &size); + * "java.base", "java/lang/String.class", is_preview_mode, &size); */ extern "C" JNIEXPORT JImageLocationRef JIMAGE_FindResource(JImageFile* image, - const char* module_name, const char* version, const char* name, + const char* module_name, const char* name, bool is_preview_mode, jlong* size) { + static const char str_modules[] = "modules"; + static const char str_packages[] = "packages"; + static const char preview_infix[] = "/META-INF/preview"; + + size_t module_name_len = strlen(module_name); + size_t name_len = strlen(name); + size_t preview_infix_len = strlen(preview_infix); + + // TBD: assert(module_name_len > 0 && "module name must be non-empty"); + assert(name_len > 0 && "name must non-empty"); + + // Do not attempt to lookup anything of the form /modules/... or /packages/... + if (strncmp(module_name, str_modules, sizeof(str_modules)) == 0 + || strncmp(module_name, str_packages, sizeof(str_packages)) == 0) { + return 0L; + } + // If the preview mode version of the path string is too long for the buffer, + // return not found (even when not in preview mode). + if (1 + module_name_len + preview_infix_len + 1 + name_len + 1 > IMAGE_MAX_PATH) { + return 0L; + } + // Concatenate to get full path - char fullpath[IMAGE_MAX_PATH]; - size_t moduleNameLen = strlen(module_name); - size_t nameLen = strlen(name); - size_t index; + char name_buffer[IMAGE_MAX_PATH]; + char* path; + { // Write the buffer with room to prepend the preview mode infix + // at the start (saves copying the trailing name part twice). + size_t index = preview_infix_len; + name_buffer[index++] = '/'; + memcpy(&name_buffer[index], module_name, module_name_len); + index += module_name_len; + name_buffer[index++] = '/'; + memcpy(&name_buffer[index], name, name_len); + index += name_len; + name_buffer[index++] = '\0'; + // Path begins at the leading '/' (not the start of the buffer). + path = &name_buffer[preview_infix_len]; + } - // TBD: assert(moduleNameLen > 0 && "module name must be non-empty"); - assert(nameLen > 0 && "name must non-empty"); + // find_location_index() returns the data "offset", not an index. + const ImageFileReader* image_file = (ImageFileReader*) image; + u4 locOffset = image_file->find_location_index(path, (u8*) size); + if (locOffset == 0) { + return 0L; + } + ImageLocation loc; + loc.set_data(image_file->get_location_offset_data(locOffset)); - // If the concatenated string is too long for the buffer, return not found - if (1 + moduleNameLen + 1 + nameLen + 1 > IMAGE_MAX_PATH) { + u4 flags = loc.get_preview_flags(); + // No preview flags means "a normal resource, without a preview version". + // This is the overwhelmingly common case, with or without preview mode. + if (flags == 0) { + return locOffset; + } + // Regardless of preview mode, don't return resources requested directly + // via their preview path. + if ((flags & ImageLocation::FLAGS_IS_PREVIEW_VERSION) != 0) { return 0L; } + // Even if there is a preview version, we might not want to return it. + if (!is_preview_mode || (flags & ImageLocation::FLAGS_HAS_PREVIEW_VERSION) == 0) { + return locOffset; + } + + { // Rewrite the front of the name buffer to make it a preview path. + size_t index = 0; + name_buffer[index++] = '/'; + memcpy(&name_buffer[index], module_name, module_name_len); + index += module_name_len; + memcpy(&name_buffer[index], preview_infix, preview_infix_len); + index += preview_infix_len; + // Check we copied up to the expected '/' separator. + assert(name_buffer[index] == '/' && "bad string concatenation"); + // The preview path now begins at the start of the buffer. + path = &name_buffer[0]; + } - index = 0; - fullpath[index++] = '/'; - memcpy(&fullpath[index], module_name, moduleNameLen); - index += moduleNameLen; - fullpath[index++] = '/'; - memcpy(&fullpath[index], name, nameLen); - index += nameLen; - fullpath[index++] = '\0'; - - JImageLocationRef loc = - (JImageLocationRef) ((ImageFileReader*) image)->find_location_index(fullpath, (u8*) size); - return loc; + // Lookup the preview version (which *should* exist). + return image_file->find_location_index(path, (u8*) size); } /* diff --git a/src/java.base/share/native/libjimage/jimage.hpp b/src/java.base/share/native/libjimage/jimage.hpp index a514e737b49..960d8e8ee21 100644 --- a/src/java.base/share/native/libjimage/jimage.hpp +++ b/src/java.base/share/native/libjimage/jimage.hpp @@ -98,21 +98,20 @@ typedef void (*JImageClose_t)(JImageFile* jimage); * name, a version string and the name of a class/resource, return location * information describing the resource and its size. If no resource is found, the * function returns JIMAGE_NOT_FOUND and the value of size is undefined. - * The version number should be "9.0" and is not used in locating the resource. * The resulting location does/should not have to be released. * All strings are utf-8, zero byte terminated. * * Ex. * jlong size; * JImageLocationRef location = (*JImageFindResource)(image, - * "java.base", "9.0", "java/lang/String.class", &size); + * "java.base", "java/lang/String.class", is_preview_mode, &size); */ extern "C" JNIEXPORT JImageLocationRef JIMAGE_FindResource(JImageFile* jimage, - const char* module_name, const char* version, const char* name, + const char* module_name, const char* name, bool is_preview_mode, jlong* size); typedef JImageLocationRef(*JImageFindResource_t)(JImageFile* jimage, - const char* module_name, const char* version, const char* name, + const char* module_name, const char* name, bool is_preview_mode, jlong* size);