< prev index next >

jdk/src/java.base/share/classes/java/lang/StackTraceElement.java

Print this page

        

@@ -28,11 +28,10 @@
 import jdk.internal.loader.BuiltinClassLoader;
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.misc.VM;
 import jdk.internal.module.ModuleHashes;
 
-import java.lang.module.ModuleDescriptor.Version;
 import java.lang.reflect.Layer;
 import java.lang.reflect.Module;
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Optional;

@@ -49,25 +48,27 @@
  *
  * @since  1.4
  * @author Josh Bloch
  */
 public final class StackTraceElement implements java.io.Serializable {
-    // This field is set to the compacted String representation used
-    // by StackTraceElement::toString and stored in serial form.
+
+    // For Throwables and StackWalker, the VM initially sets this field to a
+    // reference to the declaring Class.  The Class reference is used to
+    // construct the 'format' bitmap, and then is cleared.
     //
-    // This field is of Object type. VM initially sets this field to
-    // the Class object of the declaring class to build the compacted string.
-    private Object classOrLoaderModuleClassName;
+    // For STEs constructed using the public constructors, this field is not used.
+    private transient Class<?> declaringClassObject;
 
     // Normally initialized by VM
     private String classLoaderName;
     private String moduleName;
     private String moduleVersion;
     private String declaringClass;
     private String methodName;
     private String fileName;
     private int    lineNumber;
+    private byte   format = 0; // Default to show all
 
     /**
      * Creates a stack trace element representing the specified execution
      * point. The {@link #getModuleName module name} and {@link
      * #getModuleVersion module version} of the stack trace element will

@@ -254,13 +255,14 @@
     public boolean isNativeMethod() {
         return lineNumber == -2;
     }
 
     /**
-     * Returns a string representation of this stack trace element.  The
-     * format of this string depends on the implementation, but the following
-     * examples may be regarded as typical:
+     * Returns a string representation of this stack trace element.
+     *
+     * @apiNote The format of this string depends on the implementation, but the
+     * following examples may be regarded as typical:
      * <ul>
      * <li>
      *     "{@code com.foo.loader/foo@9.0/com.foo.Main.run(Main.java:101)}"
      * - See the description below.
      * </li>

@@ -307,37 +309,42 @@
      *
      * <p> If a class is defined in an <em>unnamed module</em>
      * then the second element is omitted as shown in
      * "{@code com.foo.loader//com.foo.bar.App.run(App.java:12)}".
      *
-     * If the class loader is a <a href="ClassLoader.html#builtinLoaders">
+     * <p> If the class loader is a <a href="ClassLoader.html#builtinLoaders">
      * built-in class loader</a> or is not named then the first element
      * and its following {@code "/"} are omitted as shown in
      * "{@code acme@2.1/org.acme.Lib.test(Lib.java:80)}".
      * If the first element is omitted and the module is an unnamed module,
      * the second element and its following {@code "/"} are also omitted
      * as shown in "{@code MyClass.mash(MyClass.java:9)}".
      *
+     * <p> The {@code toString} method may return two different values on two
+     * {@code StackTraceElement} instances that are
+     * {@linkplain #equals(Object) equal}, for example one created via the
+     * constructor, and one obtained from {@link java.lang.Throwable} or
+     * {@link java.lang.StackWalker.StackFrame}, where an implementation may
+     * choose to omit some element in the returned string.
+     *
      * @see    Throwable#printStackTrace()
      */
     public String toString() {
-        String s = buildLoaderModuleClassName();
-        if (s == null) {
-            // all elements will be included
-            s = "";
-            if (classLoaderName != null && !classLoaderName.isEmpty()) {
+        String s = "";
+        if (!dropClassLoaderName() && classLoaderName != null &&
+                !classLoaderName.isEmpty()) {
                 s += classLoaderName + "/";
             }
             if (moduleName != null && !moduleName.isEmpty()) {
                 s += moduleName;
 
-                if (moduleVersion != null && !moduleVersion.isEmpty()) {
+            if (!dropModuleVersion() && moduleVersion != null &&
+                    !moduleVersion.isEmpty()) {
                     s += "@" + moduleVersion;
                 }
             }
             s = s.isEmpty() ? declaringClass : s + "/" + declaringClass;
-        }
 
         return s + "." + methodName + "(" +
              (isNativeMethod() ? "Native Method)" :
               (fileName != null && lineNumber >= 0 ?
                fileName + ":" + lineNumber + ")" :

@@ -395,71 +402,57 @@
         return result;
     }
 
 
     /**
-     * Build the compacted String representation to be returned by
-     * toString method from the declaring Class object.
-     */
-    synchronized String buildLoaderModuleClassName() {
-        if (classOrLoaderModuleClassName == null)
-            return null;
-
-        if (classOrLoaderModuleClassName instanceof Class) {
-            Class<?> cls = (Class<?>)classOrLoaderModuleClassName;
-            classOrLoaderModuleClassName = toLoaderModuleClassName(cls);
-        }
-        return (String)classOrLoaderModuleClassName;
-    }
-
-    /**
-     * Returns <loader>/<module>/<fully-qualified-classname> string
-     * representation of the given class.
-     * <p>
-     * If the module is a non-upgradeable JDK module then omit
-     * its version string.
-     * <p>
-     * If the loader has no name, or if the loader is one of the built-in
-     * loaders (`boot`, `platform`, or `app`) then drop the first element
-     * (`<loader>/`).
+     * Called from of() methods to set the 'format' bitmap using the Class
+     * reference stored in declaringClassObject, and then clear the reference.
+     * 
      * <p>
-     * If the first element has been dropped and the module is unnamed
-     * then drop the second element (`<module>/`).
+     * If the module is a non-upgradeable JDK module, then set 
+     * JDK_NON_UPGRADEABLE_MODULE to omit its version string.
      * <p>
-     * If the first element is not dropped and the module is unnamed
-     * then drop `<module>`.
+     * If the loader is one of the built-in loaders (`boot`, `platform`, or `app`)
+     * then set BUILTIN_CLASS_LOADER to omit the first element (`<loader>/`).
      */
-    private static String toLoaderModuleClassName(Class<?> cls) {
+    private synchronized void computeFormat() {
+        try {
+            Class<?> cls = (Class<?>) declaringClassObject;
         ClassLoader loader = cls.getClassLoader0();
         Module m = cls.getModule();
+            byte bits = 0;
 
         // First element - class loader name
         // Call package-private ClassLoader::name method
-        String s = "";
-        if (loader != null && loader.name() != null &&
-                !(loader instanceof BuiltinClassLoader)) {
-            s = loader.name() + "/";
+
+            if (loader instanceof BuiltinClassLoader) {
+                bits |= BUILTIN_CLASS_LOADER;
         }
 
         // Second element - module name and version
-        if (m != null && m.isNamed()) {
-            s += m.getName();
-            // Include version if it is a user module or upgradeable module
-            //
-            // If it is JDK non-upgradeable module which is recorded
-            // in the hashes in java.base, omit the version.
-            if (!isHashedInJavaBase(m)) {
-                Optional<Version> ov = m.getDescriptor().version();
-                if (ov.isPresent()) {
-                    String version = "@" + ov.get().toString();
-                    s += version;
+
+            // Omit if is a JDK non-upgradeable module (recorded in the hashes
+            // in java.base)
+            if (isHashedInJavaBase(m)) {
+                bits |= JDK_NON_UPGRADEABLE_MODULE;
+            }
+            format = bits;
+        } finally {
+            // Class reference no longer needed, clear it
+            declaringClassObject = null;
                 }
             }
+
+    private static final byte BUILTIN_CLASS_LOADER       = 0x1;
+    private static final byte JDK_NON_UPGRADEABLE_MODULE = 0x2;
+
+    private boolean dropClassLoaderName() {
+        return (format & BUILTIN_CLASS_LOADER) == BUILTIN_CLASS_LOADER;
         }
 
-        // fully-qualified class name
-        return s.isEmpty() ? cls.getName() : s + "/" + cls.getName();
+    private boolean dropModuleVersion() {
+        return (format & JDK_NON_UPGRADEABLE_MODULE) == JDK_NON_UPGRADEABLE_MODULE;
     }
 
     /**
      * Returns true if the module is hashed with java.base.
      * <p>

@@ -517,11 +510,11 @@
         // VM to fill in StackTraceElement
         initStackTraceElements(stackTrace, x);
 
         // ensure the proper StackTraceElement initialization
         for (StackTraceElement ste : stackTrace) {
-            ste.buildLoaderModuleClassName();
+            ste.computeFormat();
         }
         return stackTrace;
     }
 
     /*

@@ -529,11 +522,11 @@
      */
     static StackTraceElement of(StackFrameInfo sfi) {
         StackTraceElement ste = new StackTraceElement();
         initStackTraceElement(ste, sfi);
 
-        ste.buildLoaderModuleClassName();
+        ste.computeFormat();
         return ste;
     }
 
     /*
      * Sets the given stack trace elements with the backtrace
< prev index next >