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

Reply via email to