Proposed API for JEP 259: Stack-Walking API

David Holmes david.holmes at oracle.com
Thu Nov 19 02:32:36 UTC 2015


On 18/11/2015 10:26 PM, Peter Levart wrote:
>
>
> On 11/18/2015 12:22 PM, Remi Forax wrote:
>> ----- Mail original -----
>>> De: "David Holmes" <david.holmes at oracle.com>
>>> À: "Mandy Chung" <mandy.chung at oracle.com>, "Peter Levart"
>>> <peter.levart at gmail.com>
>>> Cc: "OpenJDK Dev list" <core-libs-dev at openjdk.java.net>
>>> Envoyé: Mercredi 18 Novembre 2015 08:58:56
>>> Objet: Re: Proposed API for JEP 259: Stack-Walking API
>>>
>>> On 18/11/2015 8:42 AM, Mandy Chung wrote:
>>>>> On Nov 17, 2015, at 2:09 PM, Peter Levart <peter.levart at gmail.com>
>>>>> wrote:
>>>>>
>>>>> I think that calling getCallerClass() from implementation of
>>>>> Runnable::run
>>>>> should expect it to return a system class. It may be Thread.class or
>>>>> ThreadPoolExecutor$Worker.class or anything actually.
>>>>>
>>>> I’m now convinced that it’s not a good idea to special case it.
>>>> getCallerClass will simply return the caller frame (i.e. top-2) on the
>>>> stack and throw UOE if there is no caller frame.  The user should call
>>>> StackWalker::walk instead if this special case matters.
>>> That sounds good to me too.
>>>
>>> David
>> Looks good to me too if IllegalStateException is used instead of
>> UnsupportedOperationException.
>> UnsuppportedOperationException is used when the operation is not
>> available, here, the same code can work or not depending how it is
>> called.
>
> But IllegalStateException is dependent on some state. There's no state
> involved here (in the sense "state" is characterized in Java). My 1st

I agree with Remi. "state" doesn't have to mean fields - there are 
numerous existing examples in the JDK. Calling a method in a context 
that is invalid is an illegal state to me. IllegalThreadStateException 
would also work. But UnsupportedOperationException ... more of a stretch.

David
-----

> thought was an IllegalArgumentException. This requires some imagination
> to view the caller passed to the method as an implicit argument.
>
> There's an obscure java.util.EmptyStackException but that is reserved
> for java.util.Stack operations.
>
> If we consider the call stack to be part of the Thread state, then maybe
> java.lang.IllegalThreadStateException (a subclass of
> IllegalArgumentException) could be used...
>
> Regards, Peter
>
>> Runnable r = () -> System.out.println(stackWalker.getCallerClass());
>> new Thread(r).start() // throw ISE
>> r.run();  // prints main class
>>
>> Rémi
>>
>>>> How does this look?
>>>>
>>>> /**
>>>>    * Gets the {@code Class} object of the caller invoking the method
>>>>    * that calls this {@code getCallerClass} method.
>>>>    *
>>>>    * <p> Reflection frames, {@link java.lang.invoke.MethodHandle} and
>>>>    * hidden frames are filtered regardless of the
>>>>    * {@link Option#SHOW_REFLECT_FRAMES SHOW_REFLECT_FRAMES}
>>>>    * and {@link Option#SHOW_HIDDEN_FRAMES SHOW_HIDDEN_FRAMES} options
>>>>    * if this {@code StackWalker} has been configured.
>>>>    *
>>>>    * <p> This method throws {@code UnsupportedOperationException} if
>>>>    * this {@code StackWalker} is not configured with
>>>>    * {@link Option#RETAIN_CLASS_REFERENCE RETAIN_CLASS_REFERENCE}
>>>> option
>>>>    * or this method is called from the last frame on the stack,
>>>>    * i.e. invoked from a JNI attached thread (
>>>>    * for example, {@code static public void main} method launched by
>>>> the
>>>>    * {@code java} launcher).
>>>>    *
>>>>    * @apiNote
>>>>    * For example, {@code Util::getResourceBundle} loads a resource
>>>> bundle
>>>>    * on behalf of the caller.  It calls this {@code getCallerClass}
>>>> method
>>>>    * to find the method calling {@code Util::getResourceBundle} and
>>>> use the
>>>>    caller's
>>>>    * class loader to load the resource bundle. The caller class in this
>>>>    example
>>>>    * is the {@code MyTool} class.
>>>>    *
>>>>    * <pre>{@code
>>>>    *     class Util {
>>>>    *         private final StackWalker walker =
>>>>    StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);
>>>>    *         public ResourceBundle getResourceBundle(String
>>>> bundleName) {
>>>>    *             Class<?> caller = walker.getCallerClass();
>>>>    *             return ResourceBundle.getBundle(bundleName,
>>>>    caller.getClassLoader());
>>>>    *         }
>>>>    *     }
>>>>    *
>>>>    *     class MyTool {
>>>>    *         private void init() {
>>>>    *             ResourceBundle rb =
>>>> Util.getResourceBundle("mybundle");
>>>>    *         }
>>>>    *     }
>>>>    * }</pre>
>>>>    *
>>>>    * An equivalent way to find the caller class using the
>>>>    * {@link StackWalker#walk walk} method is as follows
>>>>    * (filtering the reflection frames, {@code MethodHandle} and hidden
>>>>    frames
>>>>    * not shown below):
>>>>    * <pre>{@code
>>>>    *     Optional<Class<?>> caller = walker.walk(s ->
>>>>    *         s.map(StackFrame::getDeclaringClass)
>>>>    *          .skip(2)
>>>>    *          .findFirst());
>>>>    * }</pre>
>>>>    *
>>>>    * When the {@code getCallerClass} method is called from a method
>>>> that
>>>>    * is the last frame on the stack, i.e. invoked from a JNI attached
>>>>    thread,
>>>>    * for example, {@code static public void main} method launched by
>>>> the
>>>>    * {@code java} launcher,
>>>>    *
>>>>    * @return {@code Class} object of the caller's caller invoking this
>>>>    method.
>>>>    *
>>>>    * @throws UnsupportedOperationException if this {@code StackWalker}
>>>>    *         is not configured with {@link
>>>> Option#RETAIN_CLASS_REFERENCE
>>>>    *         Option.RETAIN_CLASS_REFERENCE}.
>>>>    * @throws UnsupportedOperationException if there is no caller
>>>> frame, i.e.
>>>>    *         when this {@code getCallerClass} method is called from
>>>> a method
>>>>    *         which is the last frame on the stack.
>>>>    */
>>>>
>>>> Mandy
>>>>
>



More information about the core-libs-dev mailing list