Proposed API for JEP 259: Stack-Walking API
Mandy Chung
mandy.chung at oracle.com
Wed Nov 18 15:47:29 UTC 2015
I also think IllegalStateException is better than UOE for this case as getCallerClass is inappropriate to be called where there is no caller frame.
Mandy
> On Nov 18, 2015, at 5:12 AM, forax at univ-mlv.fr wrote:
>
> Hi peter,
> Given this is a stack walker, considering the stack as a state seems natural to me.
>
> Rémi
>
> ----- Mail original -----
>> De: "Peter Levart" <peter.levart at gmail.com>
>> À: "Remi Forax" <forax at univ-mlv.fr>, "David Holmes" <david.holmes at oracle.com>
>> Cc: "Mandy Chung" <mandy.chung at oracle.com>, "OpenJDK Dev list" <core-libs-dev at openjdk.java.net>
>> Envoyé: Mercredi 18 Novembre 2015 13:26:32
>> Objet: Re: Proposed API for JEP 259: Stack-Walking API
>>
>>
>>
>> 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
>> 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