Classes on the stack trace (was: getElementClass/StackTraceElement, was: @CallerSensitive public API, was: sun.reflect.Reflection.getCallerClass)

Charles Oliver Nutter headius at headius.com
Tue Jul 30 08:58:05 PDT 2013


On Tue, Jul 30, 2013 at 7:17 AM, Peter Levart <peter.levart at gmail.com> wrote:
> For outside JDK use, I think there are two main needs, which are actually
> distinct:
>
> a) the caller-sensitive methods
> b) anything else that is not caller-sensitive, but wants to fiddle with the
> call-stack
>
> For caller-sensitive methods, the approach taken with new
> Reflection.getCallerClass() is the right one, I think. There's no need to
> support a fragile API when caller-sensitivity is concerned, so the lack of
> "int" parameter, combined with annotation for marking such methods is
> correct approach, I think. The refactorings to support this change in JDK
> show that this API is adequate. The "surface" public API methods must
> capture the caller class and pass it down the internal API where it can be
> used.

This is largely what I advocated and what we do in JRuby.

First of all, we've never made any guarantees about calls to
caller-sensitive methods like Class.forName. If issues were reported,
our answer was to pass in a classloader, just as you would have to do
if you had a utility library between your user code and a
Class.forName call.

Second, the presence of a hidden API to walk the stack is not an
excuse for using it and then complaining when it is taken away. Yes
yes, Unsafe falls into this category too, but in the case of Unsafe
there's no alternative. With getCallerClass, there is an alternative:
pass down the caller class. This may not be attractive, especially
given the magic provided by getCallerClass before...but it is a
solution.

Third, for language runtimes like Groovy, it seems to me that only
*effort* is required to handle the passing down of the caller class.
If we look at the facts, we see that getCallerClass is needed to skip
intermediate frames by the runtime. So the runtime knows about these
intermediate frames and knows how many to skip. This means that the
original call is not into an uncontrolled library, but instead is into
Groovy-controlled code. Passing down the caller object or class at
that point is obviously possible. Even if the hassle of passing a new
additional parameter through the call protocol is too difficult, a
ThreadLocal could be utilized for this purpose.

For the logging frameworks, I do not have a solution other than the
same one we recommend to JRuby users: pass in the class or
classloader. I could also suggest generating a backtrace and walking
back to the appropriate element, but backtrace generation is currently
far too expensive to use in heavily-hit logging code (this should be
improved).

I will also say that I agree an official stack-walking capability
would be incredibly useful, and not just for this case. But that's a
much bigger fish to fry and it won't happen in JDK8 timeframe.

> Now that is the question for mlvm-dev mailing list: Isn't preventing almost
> all Lookup objects obtained by Lookup.in(RequestedLookupClass.class) from
> obtaining MHs of @CallerSensitive methods too restrictive?

Probably. It seems to me that @CallerSensitive is no different from
exposing private methods or fields through a MH. Perhaps it should
recalculate caller when it's called, perhaps it should calculate it at
lookup time, but not being retrievable at all seems like overkill. I
will grant that overkill was probably the quickest and safest solution
at the time.

> 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:

A new stack trace getter that provides classes would be an immense
improvement, but only if it did not have the same overhead as current
stack trace generation. Again, that needs to be fixed.

> 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.

Probably for 7 and 8. I'm pessimistic about its use, but the timeframe
for moving away from it is too short.

- Charlie


More information about the mlvm-dev mailing list