--- old/jdk/src/java.base/share/classes/java/lang/StackTraceElement.java 2016-11-02 10:10:06.000000000 -0700 +++ new/jdk/src/java.base/share/classes/java/lang/StackTraceElement.java 2016-11-02 10:10:06.000000000 -0700 @@ -25,7 +25,18 @@ package java.lang; +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; +import java.util.Set; /** * An element in a stack trace, as returned by {@link @@ -40,7 +51,15 @@ * @author Josh Bloch */ public final class StackTraceElement implements java.io.Serializable { - // Normally initialized by VM (public constructor added in 1.5) + // 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; @@ -72,19 +91,22 @@ */ public StackTraceElement(String declaringClass, String methodName, String fileName, int lineNumber) { - this(null, null, declaringClass, methodName, fileName, lineNumber); + this(null, null, null, declaringClass, methodName, fileName, lineNumber); } /** * Creates a stack trace element representing the specified execution * point. * + * @param classLoaderName the class loader name if the class loader of + * the class containing the execution point represented by + * the stack trace is named; otherwise {@code null} * @param moduleName the module name if the class containing the * execution point represented by the stack trace is in a named - * module; can be {@code null} + * module; otherwise {@code null} * @param moduleVersion the module version if the class containing the * execution point represented by the stack trace is in a named - * module that has a version; can be {@code null} + * module that has a version; otherwise {@code null} * @param declaringClass the fully qualified name of the class containing * the execution point represented by the stack trace element * @param methodName the name of the method containing the execution point @@ -97,26 +119,30 @@ * a negative number if this information is unavailable. A value * of -2 indicates that the method containing the execution point * is a native method + * * @throws NullPointerException if {@code declaringClass} is {@code null} * or {@code methodName} is {@code null} + * * @since 9 */ - public StackTraceElement(String moduleName, String moduleVersion, + public StackTraceElement(String classLoaderName, + String moduleName, String moduleVersion, String declaringClass, String methodName, String fileName, int lineNumber) { - this.moduleName = moduleName; - this.moduleVersion = moduleVersion; - this.declaringClass = Objects.requireNonNull(declaringClass, "Declaring class is null"); - this.methodName = Objects.requireNonNull(methodName, "Method name is null"); - this.fileName = fileName; - this.lineNumber = lineNumber; + this.classLoaderName = classLoaderName; + this.moduleName = moduleName; + this.moduleVersion = moduleVersion; + this.declaringClass = Objects.requireNonNull(declaringClass, "Declaring class is null"); + this.methodName = Objects.requireNonNull(methodName, "Method name is null"); + this.fileName = fileName; + this.lineNumber = lineNumber; } - - /** - * Creates an empty stack frame element to be filled in by Throwable. + /* + * Private constructor for the factory methods to create StackTraceElement + * for Throwable and StackFrameInfo */ - StackTraceElement() { } + private StackTraceElement() {} /** * Returns the name of the source file containing the execution point @@ -178,6 +204,21 @@ } /** + * Returns the name of the class loader of the class containing the + * execution point represented by this stack trace element. + * + * @return the name of the class loader of the class containing the execution + * point represented by this stack trace element; {@code null} + * if the class loader is not named. + * + * @since 9 + * @see java.lang.ClassLoader#getName() + */ + public String getClassLoaderName() { + return classLoaderName; + } + + /** * Returns the fully qualified name of the class containing the * execution point represented by this stack trace element. * @@ -220,38 +261,83 @@ * examples may be regarded as typical: *
The first example shows a stack trace element consisting of + * three elements, each separated by {@code "/"} followed with + * the source file name and the line number of the source line + * containing the execution point. + * + * The first element "{@code com.foo.loader}" is + * the name of the class loader. The second element "{@code foo@9.0}" + * is the module name and version. The third element is the method + * containing the execution point; "{@code com.foo.Main"}" is the + * fully-qualified class name and "{@code run}" is the name of the method. + * "{@code Main.java}" is the source file name and "{@code 101}" is + * the line number. + * + *
If a class is defined in an unnamed module + * 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 + * built-in class loader 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 mid = ""; - if (moduleName != null) { - mid = moduleName; - if (moduleVersion != null) - mid += "@" + moduleVersion; - mid += "/"; + 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 getClassName() + "." + methodName + "(" + mid + + + return s + "." + methodName + "(" + (isNativeMethod() ? "Native Method)" : (fileName != null && lineNumber >= 0 ? fileName + ":" + lineNumber + ")" : @@ -264,12 +350,14 @@ * point as this instance. Two stack trace elements {@code a} and * {@code b} are equal if and only if: *
{@code - * equals(a.getFileName(), b.getFileName()) && - * a.getLineNumber() == b.getLineNumber()) && + * equals(a.getClassLoaderName(), b.getClassLoaderName()) && * equals(a.getModuleName(), b.getModuleName()) && * equals(a.getModuleVersion(), b.getModuleVersion()) && * equals(a.getClassName(), b.getClassName()) && * equals(a.getMethodName(), b.getMethodName()) + * equals(a.getFileName(), b.getFileName()) && + * a.getLineNumber() == b.getLineNumber() + * * }* where {@code equals} has the semantics of {@link * java.util.Objects#equals(Object, Object) Objects.equals}. @@ -285,9 +373,10 @@ if (!(obj instanceof StackTraceElement)) return false; StackTraceElement e = (StackTraceElement)obj; - return e.declaringClass.equals(declaringClass) && + return Objects.equals(classLoaderName, e.classLoaderName) && Objects.equals(moduleName, e.moduleName) && Objects.equals(moduleVersion, e.moduleVersion) && + e.declaringClass.equals(declaringClass) && e.lineNumber == lineNumber && Objects.equals(methodName, e.methodName) && Objects.equals(fileName, e.fileName); @@ -298,6 +387,7 @@ */ public int hashCode() { int result = 31*declaringClass.hashCode() + methodName.hashCode(); + result = 31*result + Objects.hashCode(classLoaderName); result = 31*result + Objects.hashCode(moduleName); result = 31*result + Objects.hashCode(moduleVersion); result = 31*result + Objects.hashCode(fileName); @@ -305,5 +395,154 @@ 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
+ * If the module is a non-upgradeable JDK module then omit + * its version string. + *
+ * 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
+ * (`
+ * If the first element has been dropped and the module is unnamed
+ * then drop the second element (`
+ * If the first element is not dropped and the module is unnamed
+ * then drop `
+ * This method returns false when running on the exploded image
+ * since JDK modules are not hashed. They have no Version attribute
+ * and so "@