Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8222071

Class.forName may return a reference to a loaded but not linked Class

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Approved
    • Icon: P3 P3
    • 14
    • core-libs
    • None
    • behavioral
    • low
    • Hide
      There is no source incompatibility, as the methods are already documented to throw LinkageError.

      Behaviorally, the methods in question could start throwing LinkageErrors that were not thrown before in the case of a class with linking errors was loaded, but never used. For code not expecting a LinkageError, the -XX flag can be used to restore the previous behavior, giving a chance for the code to be fixed.
      Show
      There is no source incompatibility, as the methods are already documented to throw LinkageError. Behaviorally, the methods in question could start throwing LinkageErrors that were not thrown before in the case of a class with linking errors was loaded, but never used. For code not expecting a LinkageError, the -XX flag can be used to restore the previous behavior, giving a chance for the code to be fixed.
    • Java API, add/remove/modify command line option

      Summary

      Two variants of the Class.forName() method:

      • Class.forName(String,boolean,ClassLoader) (using false for initialize)
      • Class.forName(Module,String)

      are specified to locate, load, and link a Class, but may return classes that have not been linked.

      Problem

      The 3-arg Class.forName(String,boolean,ClassLoader) has a boolean argument to specify whether or not to initialize the loaded class. If true, the class is linked, and then initialized. If false, the Class should still be linked, per the spec ("this method attempts to locate, load, and link the class or interface"). But as it is, the Class does not get linked until it is used.

      This is not a problem for most usages, but can be for debugging/servicability/etc. (JDK-8181144, for example - a class loaded and unused in this way will not show up in VirtualMachine.allClasses().)

      Conforming to the spec and linking classes in Class.forName() would change long-standing behavior. Various LinkageError subtypes can be thrown at class link time. Linking during Class.forName() could change such exceptions to be thrown earlier. Consider a Class loaded (and not initialized) by forName(), but never used. There could be latent linkage errors which have never been encountered. With this change in behavior, LinkageError would now be thrown from Class.forName().

      Solution

      The behavior of the following methods will be updated to conform to the spec, linking the class before returning:

      • Class.forName(String,boolean,ClassLoader) (when initialize is false)
      • Class.forName(Module,String)
      • MethodHandles.Lookup.findClass(String)

      Lookup.findClass() and the 2-arg Class.forName() were added in JDK 9, so they're unlikely to be widely used.

      The 3-arg Class.forName() is already specified to throw LinkageError (all 3 above methods are), so developers should expect that it could occur. However such checking is not required by the language (unchecked Error). A -XX:+ClassForNameDeferLinking flag will be added to restore the previous behavior.

      The spec for MethodHandles.Lookup.findClass(String) will be updated to mention that it loads and links the class.

      An alternative solution that was considered would be to leave the implementation as is, update the spec to match the current behavior of not linking the class, and add a new, 4-arg Class.forName() variant with two boolean arguments - one to request linking (or not), and one to request initialization (or not).

      Changing the behavior is preferred to adding a likely rarely-used method, given that the compatibility risk is believed to be manageable.

      Specification

      java.lang.invoke.MethodHandles.Lookup

      in src/java.base/share/classes/java/lang/invoke/MethodHandles.java:

      @@ -1927,12 +1927,17 @@
                   /**
          -         * Looks up a class by name from the lookup context defined by this {@code Lookup} object. The static
          +         * Looks up a class by name from the lookup context defined by this {@code Lookup} object.
          +         * This method attempts to locate, load, and link the class, and then determines whether
          +         * the class is accessible to this {@code Lookup} object.  The static
                    * initializer of the class is not run.
                    * <p>
                    * The lookup context here is determined by the {@linkplain #lookupClass() lookup class}, its class
          -         * loader, and the {@linkplain #lookupModes() lookup modes}. In particular, the method first attempts to
          -         * load the requested class, and then determines whether the class is accessible to this lookup object.
          +         * loader, and the {@linkplain #lookupModes() lookup modes}.
          +         * <p>
          +         * Note that this method throws errors related to loading and linking as
          +         * specified in Sections 12.2 and 12.3 of <em>The Java Language
          +         * Specification</em>.
                         *
      @@ -1944,6 +1949,9 @@
      +         *
      +         * @jls 12.2 Loading of Classes and Interfaces
      +         * @jls 12.3 Linking of Classes and Interfaces
                * @since 9
                */
               public Class<?> findClass(String targetName) throws ClassNotFoundException, IllegalAccessException

      java.lang.Class

      in src/java.base/share/classes/java/lang/Class.java:

      @@ -390,10 +390,14 @@
      +     *
      +     * @jls 12.2 Loading of Classes and Interfaces
      +     * @jls 12.3 Linking of Classes and Interfaces
      +     * @jls 12.4 Initialization of Classes and Interfaces
            * @since     1.2
            */
           @CallerSensitive
           public static Class<?> forName(String name, boolean initialize,
                                          ClassLoader loader)
          ...
          @@ -436,10 +440,14 @@
                * accessible to its caller. </p>
                *
          +     * <p> Note that this method throws errors related to loading and linking as
          +     * specified in Sections 12.2 and 12.3 of <em>The Java Language
          +     * Specification</em>.
          +     * 
                * @apiNote
                * This method returns {@code null} on failure rather than
          @@ -463,10 +471,12 @@
                *         in a module.</li>
                *         </ul>
                *
          +     * @jls 12.2 Loading of Classes and Interfaces
          +     * @jls 12.3 Linking of Classes and Interfaces
                * @since 9
                * @spec JPMS
                */
               @CallerSensitive
               public static Class<?> forName(Module module, String name)

      A new JDK-specific VM product flag is added for backward compatibility:

      Setting -XX:+ClassForNameDeferLinking will restore the previous behavior, where a class is not necessarily linked by Class.forName(), but when the class is first used.

            bchristi Brent Christian
            alanb Alan Bateman
            Alan Bateman, David Holmes, Mandy Chung (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            6 Start watching this issue

              Created:
              Updated:
              Resolved: