[PATCH] 4851444: Exposing sun.reflect.Reflection#getCallerClass as a public API in Java 8

Mandy Chung mandy.chung at oracle.com
Fri Sep 20 18:46:44 UTC 2013


Jochen,

On 9/20/13 4:19 AM, Jochen Theodorou wrote:
> Am 20.09.2013 12:05, schrieb Peter Levart:
> [...]
>> The use-cases described used getCallerClass(int depth) repeatedly to
>> find a caller by iterating over a range of depths and calling
>> getCallerClass(depth). You can use either Thread.walkStack or
>> Thread.firstCaller for implementing those use-cases.
>
> first Caller is only going back one, or not? Not useful for us in the 
> cases we need this. I wouldn't say the API through getCallerClass(int) 
> was optimal for us, I think the new one can be better... I only miss 
> (or didn't get how) the ability not to have to walk the entire stack, 
> without knowing before how deep I have to go

What I understand is that Groovy [1] is similar to what 
j.u.logging.LogRecord.inferCaller that skips the frames of the internal 
Groovy implementation as well as reflection classes.

   
    Thread.firstCaller(e -> {return !isLoggerImplFrame(e.getClassName()); }, Function::identity);

For example,

	java.lang.Thread.firstCaller
	java.util.logging.LogRecord.inferCaller
	java.util.logging.LogRecord.getSourceClassName
	java.util.logging.SimpleFormatter.format
	java.util.logging.StreamHandler.publish
	java.util.logging.ConsoleHandler.publish
	java.util.logging.Logger.log
	java.util.logging.Logger.doLog
	java.util.logging.Logger.log
	java.util.logging.Logger.severe
         sun.reflect.....
         sun.reflect.....
         java.lang.reflect.Method.invoke
	Hi.foo  <----- the first caller that isLoggerImplFrame returns true.
         ....

The firstCaller method will stop at the first non-reflection frame that 
the given predicate returns true.

Thread.getCaller method is equivalent to calling
       Thread.firstCaller(skipToSecondPredicate, Function::identity);

I have some trouble in expressing the skipToSecondPredicate without side 
effect in lambda.  With the help from Paul Sandoz (thanks Paul), if I 
had a stack stream, Thread.getCaller() method would be like this:
    stream.filter(e -> return 
REFLECTION_CLASSES.contains(e->getClassName()))
              .skip(2).findFirst();

With the example you gave below, I think a method that takes a parameter 
to skip the number of frames that matches the predicate will be useful:

    findCaller(Predicate<StackFrameInfo> predicate,
               int skips,
               Function<StackFrameInfo> function)

Thread.getCaller is equivalent to calling
       findCaller(e -> { return true; }, 2, function);


See below for your example.

[1] 
https://github.com/groovy/groovy-core/blob/master/src/main/org/codehaus/groovy/reflection/ReflectionUtils.java#L105

>
>> Maybe the following method would be handy to optimize search when we
>> know that we want to skip 1st N frames before starting testing with
>> predicate:
>>
>> public static <T> T firstCaller(int startDepth,
>>                                  Predicate<StackFrameInfo> predicate,
>> Function<StackFrameInfo,T> function) {
>>
>>
>> Reflection.getCallerClass(depth)
>>
>> then becomes:
>>
>> Thread.firstCaller(depth, f -> true, StackFrameInfo::getDeclaringClass);
>>
>>
>> Hm...
>
> that is I think a usecase for some... as I said, getCallerClass(int) 
> is not really ideal for us either. More ideal in this style would be 
> for us
>
>>  public static <T> T findCaller(Predicate<StackFrameInfo> predicate,
>>                                 Function<StackFrameInfo,T> function)
>
> with the predicate indicating when to stop.. though the usage of this 
> is not that nice:
>
>> Class getCallerClass(final int nonSkippedFramesDepth) {
>>    return findCaller(new Predicate<StackFrameInfo>() {
>>         int depth = 0;
>>         boolean test(StackFrameInfo info) {
>>                     if (haveToSkip(info.getDeclaringClass())) return 
>> false;
>>                     depth++;
>>                     if (depth>=nonSkippedFramesDepth) return 
>> info.getDeclaringClass();
>>                 }
>>           }, StackFrameInfo::getDeclaringClass());
>> }
>
> Have you guys though about exposing the StackStream instead? Then you 
> could use all the existing JDK method for streams on that, which gives 
> you a much more flexible API. I could then for example change the 
> Stream of StackFrameInfo into one of Class.

Exposing a StackStream API means that you need to eagerly walk the stack 
and copy the stack frames to it before it returns.  I agree it is a much 
more flexible API.  On the other hand, stack walking is sequential and 
ordered and a stack stream will be traversed as in an iterator.

What about a findCaller method that takes a parameter to indicate how 
many times you skip over the matching elements before applying the function:

    Thread.findCaller(info -> {return !haveToSkip(info.getDeclaringClass());},
                      nonSkippedFramesDepth,
                      StackFrameInfo::getDeclaringClass());

Getting a StackStream instance would be performant only if you know the 
number of frames that guarantees to find the matching frame. The 
Thread.findCaller method allows the stack walk engine to perform the 
lazy computation without doing the eager copying.

Mandy



More information about the core-libs-dev mailing list