All,

In the last two months, there have been a number of discussions surrounding 
stack traces, Classes on the stack trace, and caller classes [1], [2], [3]. 
These are all related discussions and the solution to them is equally related, 
so I wanted to consolidate it all into this one discussion where I hope we can 
finalize on a solution and get it implemented for Java 8.

In a nut shell, here are the underlying needs that I have seen expressed 
through many, many messages:

- Some code needs to get the Class of the caller of the current method, 
skipping any reflection methods.
- Some code needs to get the Class of the caller /n/ stack frames before the 
current method, skipping any reflection methods.
- Some code needs to get the current stack trace, populated with Classes, 
Executables, file names, line numbers, and native flags instead of the String 
class names and String method names in StackTraceElement. This /should/ include 
any reflection methods, just like StackTraceElement[]s.
- Some code needs to get the stack trace from when a Throwable was created, 
populated with Classes, Executables, file names, line numbers, and native flags 
instead of the String class names and String method names in StackTraceElement. 
This /should/ include any reflection methods, just like StackTraceElement[]s.
- There needs to be a reflection way to achieve all of this since some 
libraries (e.g., Log4j) need to be compiled against Java 6 but run on 7 and 8 
(and thus can't use @CallerSensitive).

I believe the solutions to these needs are all related. Importantly, I think it 
is very important that action be taken in Java 8 due to the changes made to 
sun.reflect.Reflection#getCallerClass(...). While we all understand that 
relying on private sun.* APIs is not safe, the fact is that many people have 
relied on sun.reflect.Reflection#getCallerClass(...) due to the fact that there 
is simply no other way to do this in the standard API. This includes Log4j 2, 
Logback, SLF4j, and Groovy, some features of which will stop working correctly 
in Java 7 >= u25.

I would point out that this could all easily be solved simply by adding a 
getElementClass() method to StackTraceElement, but there was strong opposition 
to this, largely due to serialization issues. Since that is apparently not an 
option, I propose the following API, based on the various discussions in the 
last two months, StackTraceElement, and the API that .NET provides to achieve 
the same needs as listed above:

CallerSensitive.java:
package java.lang;

/** Previously private API, now public */
public @interface CallerSensitive {
    ...
}

StackTraceFrame.java:
package java.lang;

import java.util.Objects.

public final class StackTraceFrame {
    private final Class<?> declaringClass;
    private final Executable executable;
    private final String fileName;
    private final int lineNumber;

    public StackTraceFrame(Class<?> declaringClass, Executable executable, 
String fileName, int lineNumber) {
        this.declaringClass = Objects.requireNonNull(declaringClass, "Declaring 
class is null");
        this.executable = Objects.requireNonNull(executable, "Executable is 
null");
        this.fileName = fileName;
        this.lineNumber = lineNumber;
    }

    public Class<?> getDeclaringClass() {
        return this.declaringClass;
    }

    public Executable getExecutable() {
        return this.executable;
    }

    public String getFileName() {
        return this.fileName;
    }

    public int getLineNumber() {
        return this.lineNumber;
    }

    public boolean isNative() {
        return this.lineNumber == -2;
    }

    public String toString() { /* Same as StackTraceElement */ }
    public boolean equals() { /* Ditto */ }
    public int hashCode() { /* Ditto */ }

    /** Uses @CallerSensitive */
    public static native StackTraceFrame getCallerFrame();

    /** Works like Java < 7u25 sun.reflect.Reflection#getCallerClass() */
    public static native StackTraceFrame getCallerFrame(int skipFrames);

    public static native StackTraceFrame[] getCurrentStackTrace();
}

Throwable.java:
package java.lang;

...

public class Throwable {
    ...
    public synchronized Throwable fillInStackTraceFrames() { ... }

    private native Throwable fillInStackTraceFrames(int dummy);

    public StackTraceFrame[] getStackTraceFrames() {
        return this.getOurStackTraceFrames().clone();
    }

    private synchronized StackTraceFrame[] getOurStackTraceFrames() { ... }
    ...
}

Furthermore, I propose that we restore the behavior of 
sun.reflect.Reflection#getCallerClass(int) /just for Java 7/ since the proposed 
above solution cannot be added to Java 7.

I would love if we could quickly coalesce around this solution or a derivative 
thereof so that it can be implemented before Feature Complete. The absence of 
any replacement or alternative for sun.reflect.Reflection#getCallerClass(int) 
will be a serious issue in Java 8 that will cause hardships for many projects.

[1] http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-June/018049.html
[2] http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-June/018349.html, 
http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-July/019098.html
[3] http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-July/018855.html

Reply via email to