Code Review for JEP 259: Stack-Walking API (original) (raw)
Peter Levart peter.levart at gmail.com
Tue Nov 17 20:57:28 UTC 2015
- Previous message: Code Review for JEP 259: Stack-Walking API
- Next message: Code Review for JEP 259: Stack-Walking API
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
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 *
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 *
{@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 * }
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 *
{@code 503 * Class caller = walker.walk(s -> 504 * s.map(StackFrame::getDeclaringClass) 505 * .skip(2) 506 * .findFirst()); 507 * }
Stream.findFirst() returns Optional, 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 *
{@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 * }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
- Previous message: Code Review for JEP 259: Stack-Walking API
- Next message: Code Review for JEP 259: Stack-Walking API
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]