< prev index next >
jdk/src/java.base/share/classes/java/lang/StackTraceElement.java
Print this page
*** 28,38 ****
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;
--- 28,37 ----
*** 49,73 ****
*
* @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.
//
! // 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;
// 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;
/**
* 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
--- 48,74 ----
*
* @since 1.4
* @author Josh Bloch
*/
public final class StackTraceElement implements java.io.Serializable {
!
! // 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.
//
! // 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,266 ****
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:
* <ul>
* <li>
* "{@code com.foo.loader/foo@9.0/com.foo.Main.run(Main.java:101)}"
* - See the description below.
* </li>
--- 255,268 ----
public boolean isNativeMethod() {
return lineNumber == -2;
}
/**
! * 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,343 ****
*
* <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">
* 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)}".
*
* @see Throwable#printStackTrace()
*/
public String toString() {
! String s = buildLoaderModuleClassName();
! if (s == null) {
! // all elements will be included
! s = "";
! if (classLoaderName != null && !classLoaderName.isEmpty()) {
s += classLoaderName + "/";
}
if (moduleName != null && !moduleName.isEmpty()) {
s += moduleName;
! if (moduleVersion != null && !moduleVersion.isEmpty()) {
s += "@" + moduleVersion;
}
}
s = s.isEmpty() ? declaringClass : s + "/" + declaringClass;
- }
return s + "." + methodName + "(" +
(isNativeMethod() ? "Native Method)" :
(fileName != null && lineNumber >= 0 ?
fileName + ":" + lineNumber + ")" :
--- 309,350 ----
*
* <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)}".
*
! * <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 = "";
! if (!dropClassLoaderName() && classLoaderName != null &&
! !classLoaderName.isEmpty()) {
s += classLoaderName + "/";
}
if (moduleName != null && !moduleName.isEmpty()) {
s += moduleName;
! 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,465 ****
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>/`).
* <p>
! * If the first element has been dropped and the module is unnamed
! * then drop the second element (`<module>/`).
* <p>
! * If the first element is not dropped and the module is unnamed
! * then drop `<module>`.
*/
! private static String toLoaderModuleClassName(Class<?> cls) {
ClassLoader loader = cls.getClassLoader0();
Module m = cls.getModule();
// 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() + "/";
}
// 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;
}
}
}
! // fully-qualified class name
! return s.isEmpty() ? cls.getName() : s + "/" + cls.getName();
}
/**
* Returns true if the module is hashed with java.base.
* <p>
--- 402,458 ----
return result;
}
/**
! * Called from of() methods to set the 'format' bitmap using the Class
! * reference stored in declaringClassObject, and then clear the reference.
! *
* <p>
! * If the module is a non-upgradeable JDK module, then set
! * JDK_NON_UPGRADEABLE_MODULE to omit its version string.
* <p>
! * 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 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
!
! if (loader instanceof BuiltinClassLoader) {
! bits |= BUILTIN_CLASS_LOADER;
}
// Second element - module name and 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;
}
! 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,527 ****
// VM to fill in StackTraceElement
initStackTraceElements(stackTrace, x);
// ensure the proper StackTraceElement initialization
for (StackTraceElement ste : stackTrace) {
! ste.buildLoaderModuleClassName();
}
return stackTrace;
}
/*
--- 510,520 ----
// VM to fill in StackTraceElement
initStackTraceElements(stackTrace, x);
// ensure the proper StackTraceElement initialization
for (StackTraceElement ste : stackTrace) {
! ste.computeFormat();
}
return stackTrace;
}
/*
*** 529,539 ****
*/
static StackTraceElement of(StackFrameInfo sfi) {
StackTraceElement ste = new StackTraceElement();
initStackTraceElement(ste, sfi);
! ste.buildLoaderModuleClassName();
return ste;
}
/*
* Sets the given stack trace elements with the backtrace
--- 522,532 ----
*/
static StackTraceElement of(StackFrameInfo sfi) {
StackTraceElement ste = new StackTraceElement();
initStackTraceElement(ste, sfi);
! ste.computeFormat();
return ste;
}
/*
* Sets the given stack trace elements with the backtrace
< prev index next >