< prev index next >

src/java.base/share/classes/jdk/internal/jimage/ImageLocation.java

Print this page
*** 1,7 ***
  /*
!  * Copyright (c) 2014, 2021, 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
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
--- 1,7 ---
  /*
!  * Copyright (c) 2014, 2025, 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
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this

*** 24,29 ***
   */
  
  package jdk.internal.jimage;
  
  import java.nio.ByteBuffer;
  import java.util.Objects;
  
  /**
   * @implNote This class needs to maintain JDK 8 source compatibility.
   *
   * It is used internally in the JDK to implement jimage/jrtfs access,
   * but also compiled and delivered as part of the jrtfs.jar to support access
   * to the jimage file provided by the shipped JDK by tools running on JDK 8.
   */
  public class ImageLocation {
      public static final int ATTRIBUTE_END = 0;
      public static final int ATTRIBUTE_MODULE = 1;
      public static final int ATTRIBUTE_PARENT = 2;
      public static final int ATTRIBUTE_BASE = 3;
      public static final int ATTRIBUTE_EXTENSION = 4;
      public static final int ATTRIBUTE_OFFSET = 5;
      public static final int ATTRIBUTE_COMPRESSED = 6;
      public static final int ATTRIBUTE_UNCOMPRESSED = 7;
!     public static final int ATTRIBUTE_COUNT = 8;
  
      protected final long[] attributes;
  
      protected final ImageStrings strings;
  
--- 24,185 ---
   */
  
  package jdk.internal.jimage;
  
  import java.nio.ByteBuffer;
+ import java.util.List;
  import java.util.Objects;
+ import java.util.function.Predicate;
  
  /**
   * @implNote This class needs to maintain JDK 8 source compatibility.
   *
   * It is used internally in the JDK to implement jimage/jrtfs access,
   * but also compiled and delivered as part of the jrtfs.jar to support access
   * to the jimage file provided by the shipped JDK by tools running on JDK 8.
   */
  public class ImageLocation {
+     // Also defined in src/java.base/share/native/libjimage/imageFile.hpp
+ 
+     /** End of attribute stream marker. */
      public static final int ATTRIBUTE_END = 0;
+     /** String table offset of module name. */
      public static final int ATTRIBUTE_MODULE = 1;
+     /** String table offset of resource path parent. */
      public static final int ATTRIBUTE_PARENT = 2;
+     /** String table offset of resource path base. */
      public static final int ATTRIBUTE_BASE = 3;
+     /** String table offset of resource path extension. */
      public static final int ATTRIBUTE_EXTENSION = 4;
+     /** Container byte offset of resource. */
      public static final int ATTRIBUTE_OFFSET = 5;
+     /** In-image byte size of the compressed resource. */
      public static final int ATTRIBUTE_COMPRESSED = 6;
+     /** In-memory byte size of the uncompressed resource. */
      public static final int ATTRIBUTE_UNCOMPRESSED = 7;
!     /** Flags relating to preview mode resources. */
+     public static final int ATTRIBUTE_PREVIEW_FLAGS = 8;
+     /** Number of attribute kinds. */
+     public static final int ATTRIBUTE_COUNT = 9;
+ 
+     // Flag masks for the ATTRIBUTE_PREVIEW_FLAGS attribute. Defined so
+     // that zero is the overwhelmingly common case for normal resources.
+ 
+     /**
+      * Indicates that a non-preview location is associated with preview
+      * resources.
+      *
+      * <p>This can apply to both resources and directories in the
+      * {@code /modules/xxx/...} namespace, as well as {@code /packages/xxx}
+      * directories.
+      *
+      * <p>For {@code /packages/xxx} directories, it indicates that the package
+      * has preview resources in one of the modules in which it exists.
+      */
+     private static final int FLAGS_HAS_PREVIEW_VERSION = 0x1;
+     /**
+      * Set on all locations in the {@code /modules/xxx/META-INF/preview/...}
+      * namespace.
+      *
+      * <p>This flag is mutually exclusive with {@link #FLAGS_HAS_PREVIEW_VERSION}.
+      */
+     private static final int FLAGS_IS_PREVIEW_VERSION = 0x2;
+     /**
+      * Indicates that a location only exists due to preview resources.
+      *
+      * <p>This can apply to both resources and directories in the
+      * {@code /modules/xxx/...} namespace, as well as {@code /packages/xxx}
+      * directories.
+      *
+      * <p>For {@code /packages/xxx} directories it indicates that, for every
+      * module in which the package exists, it is preview only.
+      *
+      * <p>This flag is mutually exclusive with {@link #FLAGS_HAS_PREVIEW_VERSION}
+      * and need not imply that {@link #FLAGS_IS_PREVIEW_VERSION} is set (i.e.
+      * for {@code /packages/xxx} directories).
+      */
+     private static final int FLAGS_IS_PREVIEW_ONLY = 0x4;
+ 
+     // Also used in ImageReader.
+     static final String MODULES_PREFIX = "/modules";
+     static final String PACKAGES_PREFIX = "/packages";
+     static final String PREVIEW_INFIX = "/META-INF/preview";
+ 
+     /**
+      * Helper function to calculate preview flags (ATTRIBUTE_PREVIEW_FLAGS).
+      *
+      * <p>Since preview flags are calculated separately for resource nodes and
+      * directory nodes (in two quite different places) it's useful to have a
+      * common helper.
+      *
+      * <p>Based on the entry name, the flags are:
+      * <ul>
+      *     <li>{@code "[/modules]/<module>/<path>"} normal resource or directory:<br>
+      *     Zero, or {@code FLAGS_HAS_PREVIEW_VERSION} if a preview entry exists.
+      *     <li>{@code "[/modules]/<module>/META-INF/preview/<path>"} preview
+      *     resource or directory:<br>
+      *     {@code FLAGS_IS_PREVIEW_VERSION}, and additionally {@code
+      *     FLAGS_IS_PREVIEW_ONLY} if no normal version of the resource exists.
+      *     <li>In all other cases, returned flags are zero (note that {@code
+      *     "/packages/xxx"} entries may have flags, but these are calculated
+      *     elsewhere).
+      * </ul>
+      *
+      * @param name the jimage name of the resource or directory.
+      * @param hasEntry a predicate for jimage names returning whether an entry
+      *     is present.
+      * @return flags for the ATTRIBUTE_PREVIEW_FLAGS attribute.
+      */
+     public static int getFlags(String name, Predicate<String> hasEntry) {
+         if (name.startsWith(PACKAGES_PREFIX + "/")) {
+             throw new IllegalArgumentException(
+                     "Package sub-directory flags handled separately: " + name);
+         }
+         // Find suffix for either '/modules/xxx/suffix' or '/xxx/suffix' paths.
+         int idx = name.startsWith(MODULES_PREFIX + "/") ? MODULES_PREFIX.length() + 1 : 1;
+         int suffixStart = name.indexOf('/', idx);
+         if (suffixStart == -1) {
+             // No flags for '[/modules]/xxx' paths (esp. '/modules', '/packages').
+             // '/packages/xxx' entries have flags, but not calculated here.
+             return 0;
+         }
+         // Prefix is either '/modules/xxx' or '/xxx', and suffix starts with '/'.
+         String prefix = name.substring(0, suffixStart);
+         String suffix = name.substring(suffixStart);
+         if (suffix.startsWith(PREVIEW_INFIX + "/")) {
+             // Preview resources/directories.
+             String nonPreviewName = prefix + suffix.substring(PREVIEW_INFIX.length());
+             return FLAGS_IS_PREVIEW_VERSION
+                     | (hasEntry.test(nonPreviewName) ? 0 : FLAGS_IS_PREVIEW_ONLY);
+         } else if (!suffix.startsWith("/META-INF/")) {
+             // Non-preview resources/directories.
+             String previewName = prefix + PREVIEW_INFIX + suffix;
+             return hasEntry.test(previewName) ? FLAGS_HAS_PREVIEW_VERSION : 0;
+         } else {
+             // Suffix is '/META-INF/xxx' and no preview version is even possible.
+             return 0;
+         }
+     }
+ 
+     /**
+      * Helper function to calculate package flags for {@code "/packages/xxx"}
+      * directory entries.
+      *
+      * <p>Based on the module references, the flags are:
+      * <ul>
+      *     <li>{@code FLAGS_HAS_PREVIEW_VERSION} if <em>any</em> referenced
+      *     package has a preview version.
+      *     <li>{@code FLAGS_IS_PREVIEW_ONLY} if <em>all</em> referenced packages
+      *     are preview only.
+      * </ul>
+      *
+      * @return package flags for {@code "/packages/xxx"} directory entries.
+      */
+     public static int getPackageFlags(List<ModuleReference> moduleReferences) {
+         boolean hasPreviewVersion =
+                 moduleReferences.stream().anyMatch(ModuleReference::hasPreviewVersion);
+         boolean isPreviewOnly =
+                 moduleReferences.stream().allMatch(ModuleReference::isPreviewOnly);
+         return (hasPreviewVersion ? ImageLocation.FLAGS_HAS_PREVIEW_VERSION : 0)
+                 | (isPreviewOnly ? ImageLocation.FLAGS_IS_PREVIEW_ONLY : 0);
+     }
+ 
+     /**
+      * Tests a non-preview image location's flags to see if it has preview
+      * content associated with it.
+      */
+     public static boolean hasPreviewVersion(int flags) {
+         return (flags & FLAGS_HAS_PREVIEW_VERSION) != 0;
+     }
+ 
+     /**
+      * Tests an image location's flags to see if it only exists in preview mode.
+      */
+     public static boolean isPreviewOnly(int flags) {
+         return (flags & FLAGS_IS_PREVIEW_ONLY) != 0;
+     }
+ 
+     public enum LocationType {
+         RESOURCE, MODULES_ROOT, MODULES_DIR, PACKAGES_ROOT, PACKAGES_DIR;
+     }
  
      protected final long[] attributes;
  
      protected final ImageStrings strings;
  

*** 283,20 ***
  
      public int getExtensionOffset() {
          return (int)getAttribute(ATTRIBUTE_EXTENSION);
      }
  
      public String getFullName() {
          return getFullName(false);
      }
  
      public String getFullName(boolean modulesPrefix) {
          StringBuilder builder = new StringBuilder();
  
          if (getModuleOffset() != 0) {
              if (modulesPrefix) {
!                 builder.append("/modules");
              }
  
              builder.append('/');
              builder.append(getModule());
              builder.append('/');
--- 439,24 ---
  
      public int getExtensionOffset() {
          return (int)getAttribute(ATTRIBUTE_EXTENSION);
      }
  
+     public int getFlags() {
+         return (int) getAttribute(ATTRIBUTE_PREVIEW_FLAGS);
+     }
+ 
      public String getFullName() {
          return getFullName(false);
      }
  
      public String getFullName(boolean modulesPrefix) {
          StringBuilder builder = new StringBuilder();
  
          if (getModuleOffset() != 0) {
              if (modulesPrefix) {
!                 builder.append(MODULES_PREFIX);
              }
  
              builder.append('/');
              builder.append(getModule());
              builder.append('/');

*** 315,40 ***
          }
  
          return builder.toString();
      }
  
-     String buildName(boolean includeModule, boolean includeParent,
-             boolean includeName) {
-         StringBuilder builder = new StringBuilder();
- 
-         if (includeModule && getModuleOffset() != 0) {
-             builder.append("/modules/");
-             builder.append(getModule());
-          }
- 
-         if (includeParent && getParentOffset() != 0) {
-             builder.append('/');
-             builder.append(getParent());
-         }
- 
-         if (includeName) {
-             if (includeModule || includeParent) {
-                 builder.append('/');
-             }
- 
-             builder.append(getBase());
- 
-             if (getExtensionOffset() != 0) {
-                 builder.append('.');
-                 builder.append(getExtension());
-             }
-         }
- 
-         return builder.toString();
-    }
- 
      public long getContentOffset() {
          return getAttribute(ATTRIBUTE_OFFSET);
      }
  
      public long getCompressedSize() {
--- 475,10 ---

*** 357,10 ***
--- 487,46 ---
  
      public long getUncompressedSize() {
          return getAttribute(ATTRIBUTE_UNCOMPRESSED);
      }
  
+     // Fast (zero allocation) type determination for locations.
+     public LocationType getType() {
+         switch (getModuleOffset()) {
+             case ImageStrings.MODULES_STRING_OFFSET:
+                 // Locations in /modules/... namespace are directory entries.
+                 return LocationType.MODULES_DIR;
+             case ImageStrings.PACKAGES_STRING_OFFSET:
+                 // Locations in /packages/... namespace are always 2-level
+                 // "/packages/xxx" directories.
+                 return LocationType.PACKAGES_DIR;
+             case ImageStrings.EMPTY_STRING_OFFSET:
+                 // Only 2 choices, either the "/modules" or "/packages" root.
+                 assert isRootDir() : "Invalid root directory: " + getFullName();
+                 return getBase().charAt(1) == 'p'
+                         ? LocationType.PACKAGES_ROOT
+                         : LocationType.MODULES_ROOT;
+             default:
+                 // Anything else is /<module>/<path> and references a resource.
+                 return LocationType.RESOURCE;
+         }
+     }
+ 
+     private boolean isRootDir() {
+         if (getModuleOffset() == 0 && getParentOffset() == 0) {
+             String name = getFullName();
+             return name.equals(MODULES_PREFIX) || name.equals(PACKAGES_PREFIX);
+         }
+         return false;
+     }
+ 
+     @Override
+     public String toString() {
+         // Cannot use String.format() (too early in startup for locale code).
+         return "ImageLocation[name='" + getFullName() + "', type=" + getType() + ", flags=" + getFlags() + "]";
+     }
+ 
      static ImageLocation readFrom(BasicImageReader reader, int offset) {
          Objects.requireNonNull(reader);
          long[] attributes = reader.getAttributes(offset);
          ImageStringsReader strings = reader.getStrings();
  
< prev index next >