Code Review for JEP 259: Stack-Walking API
Peter Levart
peter.levart at gmail.com
Tue Nov 17 20:57:28 UTC 2015
Hi Mandy,
On 11/17/2015 01:13 AM, Mandy Chung wrote:
> I’d like to get the code review done by this week.
>
> I renamed the static factory method from create to getInstance since “create” implies to create a new instance but the method returns a cached instance instead. I also changed the spec of getCallerClass per [1]. There is not much change since webrev.01.
>
> Webrev:
> http://cr.openjdk.java.net/~mchung/jdk9/jep259/webrev.02
>
> javadoc:
> http://cr.openjdk.java.net/~mchung/jdk9/jep259/api/
>
> Mandy
> [1] http://mail.openjdk.java.net/pipermail/core-libs-dev/2015-November/036589.html
Just read the javadoc so-far...
There are several mistakes in getCallerClass() API Note (some probably a
left-over from previous iterations of the API):
471 * <p> If this {@code getCallerClass} method is called by the
entry point
472 * of a thread, the {@code Class} of the method invoked at
thread's start
473 * time will be returned.
Hm, I can't decipher that. What about something like:
If this {@code getCallerClass} method is called by the entry point of a
thread - a method overriding {@link Thread#run} (that's the only
possibility),
the declaring class of the method overriding {@link Thread#run} will be
returned.
Or, if you accept my latest suggestion:
If this {@code getCallerClass} method is called by the entry point of a
thread - a method overriding {@link Thread#run},
{@link IllegalArgumentException} will be thrown.
474 *
475 * @apiNote
476 * For example, {@code ResourceBundleUtil::getBundle} loads a
resource bundle
477 * on behalf of the caller. It calls this {@code
getCallerClass} method
478 * to find the method calling {@code Util::getResourceBundle}
and use the caller's
479 * class loader to load the resource bundle. The caller class
in this example
480 * is the {@code MyTool} class.
481 *
482 * <pre>{@code
483 * class ResourceBundleUtil {
484 * private final StackWalker walker =
StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);
485 * public ResourceBundle getBundle(String bundleName) {
486 * Class<?> caller = walker.getCallerClass();
487 * return ResourceBundle.getBundle(bundleName,
caller.getClassLoader());
488 * }
489 * }
490 *
491 * class MyTool {
492 * private void init() {
493 * ResourceBundle rb =
Util.getResourceBundle("mybundle");
494 * }
495 * }
496 * }</pre>
- threre's no ResourceBundle.getBundle(String, ClassLoader) method.
- Util -> ResourceBundleUtil (or ResourceBundleUtil -> Util)
497 *
498 * An equivalent way to find the caller class using the
499 * {@link StackWalker#walk walk} method is as follows
500 * (filtering the reflection frames, {@code MethodHandle} and
hidden frames
501 * not shown below):
502 * <pre>{@code
503 * Class<?> caller = walker.walk(s ->
504 * s.map(StackFrame::getDeclaringClass)
505 * .skip(2)
506 * .findFirst());
507 * }</pre>
- Stream.findFirst() returns Optional<E>, not E.
508 *
509 * When the {@code getCallerClass} method is invoked from
thread {@code t}'s
510 * entry point, i.e. {@code PrimeRun::run}, it returns {@code
PrimeRun} class
511 * that is the first stack frame below the stack frame for
{@code getCallerClass}
512 * instead.
513 *
514 * <pre>{@code
515 * class PrimeRun implements Runnable {
516 * public void run() {
517 * Class<?> c =
StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
518 * .getCallerClass();
519 * }
520 * }
521 * Thread t = new Thread(new PrimeRun()).start();
522 * }</pre>
523 *
524 * Similarly, when the {@code getCallerClass} method is called
from the
525 * {@code static public void main} method launched by the
{@code java} launcher,
526 * it returns the {@code Class} of the {@code main} method.
527 *
528 * @return {@code Class} object of the caller's caller
invoking this method;
529 * or the {@code Class} object of the method invoked by a
thread at start time.
- In above example, t's entry point is Thread::run (not PrimeRun::run).
Thread::run then delegates to PrimeRun::run:
public class Thread {
...
public void run() {
if (target != null) {
target.run();
}
}
...so this example is not really suitable to describe the effect of
invoking getCallerClass from thread's entry-point.
An example of that kind would be something like:
class PrimeThread extends Thread {
@Override public void run() {
Class<?> c = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
.getCallerClass();
}
}
Thread t = new PrimeThread().start();
There are only three situations to consider:
- the method overriding Thread::run, called as thread's entry-point
- the static main method, called by java launcher
- any method called by JNI from newly attached thread
The last is special - It's not any method called by JNI. If Java calls a
native method and that native method calls-back to Java via JNI, the
method calling native method will be on the stack and getCallerClass()
invoked from the called-back method will return it (this can be seen
when observing reflection calls that go through
sun.reflect.NativeMethodAccessorImpl).
Regards, Peter
More information about the hotspot-runtime-dev
mailing list