On Jul 30, 2013, at 4:11 PM, Nick Williams
<[email protected]> wrote:
> Quick question for those of you that know anything about @CallerSensitive...
>
> After looking at the code and experimenting some, I've discovered that
> getCallerClass() doesn't actually keep going until it finds the first method
> without @CallerSensitive. It only returns the caller of the caller. So, for
> example:
>
> Stack 1
> @CallerSensitive Reflection.getCallerClass()
> @CallerSensitive MyClass1.method1();
> MyClass2.method2();
>
> In this case, getCallerClass() returns MyClass2.class. BUT:
>
> Stack 2
> @CallerSensitive Reflection.getCallerClass()
> @CallerSensitive MyClass1.method1();
> @CallerSensitive MyClass2.method2();
> MyClass3.method3();
>
> In this case, getCallerClass() STILL returns MyClass2.class. Based on the
> plain-language meaning of @CallerSensitive, I would expect getCallerClass()
> to return MyClass3.class in the second case. But, indeed, the JavaDoc for
> Reflection.getCallerClass() says: "Returns the class of the caller of the
> method calling this method." So, then, what's the point of @CallerSensitive?
> Looking at the code:
>
> vframeStream vfst(thread);
> // Cf. LibraryCallKit::inline_native_Reflection_getCallerClass
> for (int n = 0; !vfst.at_end(); vfst.security_next(), n++) {
> Method* m = vfst.method();
> assert(m != NULL, "sanity");
> switch (n) {
> case 0:
> // This must only be called from Reflection.getCallerClass
> if (m->intrinsic_id() != vmIntrinsics::_getCallerClass) {
> THROW_MSG_NULL(vmSymbols::java_lang_InternalError(),
> "JVM_GetCallerClass must only be called from Reflection.getCallerClass");
> }
> // fall-through
> case 1:
> // Frame 0 and 1 must be caller sensitive.
> if (!m->caller_sensitive()) {
> THROW_MSG_NULL(vmSymbols::java_lang_InternalError(),
> err_msg("CallerSensitive annotation expected at frame %d", n));
> }
> break;
> default:
> if (!m->is_ignored_by_security_stack_walk()) {
> // We have reached the desired frame; return the holder class.
> return (jclass) JNIHandles::make_local(env,
> m->method_holder()->java_mirror());
> }
> break;
> }
> }
>
> It seems to me that @CallerSensitive is completely pointless. This is ALWAYS
> going to return the first non-reflection frame after frame 1, regardless of
> @CallerSensitive. If @CallerSensitive were really supposed to have an actual
> purpose, it would seem to me that the last part should be this:
>
> if (!m->is_ignored_by_security_stack_walk() && !m->caller_sensitive()) {
> // We have reached the desired frame; return the holder class.
> return (jclass) JNIHandles::make_local(env,
> m->method_holder()->java_mirror());
> }
>
> Am I completely missing the point here? I just don't see a reason for
> @CallerSensitive. The code could do the exact same thing it currently is
> without @CallerSensitive (except for enforcing that frame 1 is
> @CallerSensitive, which really isn't necessary if you aren't using it in
> further frames).
>
> Thoughts?
You are missing the second (and perhaps more important) part of this change.
Read:
http://openjdk.java.net/jeps/176
-- Chris
>
> Nick
>
> On Jul 30, 2013, at 10:33 AM, Jochen Theodorou wrote:
>
>> Am 30.07.2013 16:16, schrieb Peter Levart:
>>>
>>> On 07/30/2013 03:19 PM, Jochen Theodorou wrote:
>>>> Am 30.07.2013 14:17, schrieb Peter Levart:
>>>> [...]
>>>>> So what would give Groovy or other language runtimes headaches when all
>>>>> there was was a parameter-less getCallerClass() API? Aren't the
>>>>> intermediate frames inserted by those runtimes controlled by the
>>>>> runtimes? Couldn't the "surface" runtime-inserted methods capture the
>>>>> caller and pass it down? I guess the problem is supporting calling the
>>>>> caller-sensitive methods like Class.forName(String) and such which don't
>>>>> have the overloaded variant taking caller Class or ClassLoader as an
>>>>> argument...
>>>> Speaking for Groovy...
>>>> those intermediate frames are runtime controlled, yes, but passing down
>>>> the caller class is exactly the problem. Imagine I would suggest that
>>>> each and every method definition in Java automatically gets an
>>>> additional parameter for the caller class, just to have access to it
>>>> inside the method. You would not accept that for Java, would you? And so
>>>> we cannot accept that for Groovy if we want to keep integration with
>>>> Java...
>>>
>>> Are you talking about internal Groovy implementation (the
>>> runtime-inserted methods) or the publicly visible API?
>>
>> that's the problem, it is a mix, some internal, other not. We are going to
>> change that in Groovy 3
>>
>>> One solution for
>>> internal implementation of Groovy could be (speaking by heart since I
>>> don't know the internals of Groovy) for the "surface" public API method
>>> which doesn't have to have the special caller parameter, to capture the
>>> caller with getCallerClass() parameterless API (possibly enclosed with a
>>> quick check confirming that it might actually be needed) and bind it to
>>> a ThreadLocal, then use this ThreadLocal down at the end...
>>
>> confirming that it might actually be needed is a problem. In the old
>> fallback path we don't know what we call until after we are deep in runtime
>> code, and there it is too late. In the other paths we could mark those
>> methods in a @CallerSensitive style and do it in that case only.
>>
>>>> and the good integration with Java is one of the key points of
>>>> Groovy. Even if we make something like that @CallerSensitive and add the
>>>> parameter only in those cases, we break being able to override methods.
>>>
>>> I guess I don't know every Groovy need to obtain the caller class. I
>>> thought the problem was to support calling caller-sensitive methods in
>>> Java API (like Class.forName(String)) from within Groovy code, where
>>> there are runtime-inserted frames between the "call-site" and the target
>>> method. Are there any other needs?
>>
>> ok, there is a misunderstanding...
>>
>> if we call a Java implemented method from Groovy, which is using
>> getCallerClass() it may or may not work. In general this does not work and
>> our problem is not about that at all. With the change to let
>> getCallerClass() ignore some reflective frames it will work actually better
>> as long as we use our custom callsite caching implementation, it will not
>> work if indy is used or the fallback path.
>>
>> To be able to call a method Class#forName(String), we need to "replace" it
>> with an implementation of our own, which we do with an approach similar to
>> extension methods (only that ours can hide existing implementation methods
>> for groovy). And in there we need to get to the caller class
>>
>> Our problem though is @Grab which is an annotation to add elements to the
>> classpath while running a script.
>>
>>>> Plus, before Groovy3 is not done we have to support several call paths.
>>>> And the oldest one, which is still a fallback, does not support
>>>> transporting the caller class through the runtime layers at all.
>>>> Changing here is a breaking change.
>>>
>>> Could you describe those call-paths? Examples of Groovy code and to what
>>> it gets translated (equivalent Java code at call site) with a brief
>>> description of what each intermediate layer (between the call-site and
>>> the target method) does and at which point the caller class is extracted...
>>
>> the code generated at the call site depends on several factors actually...
>> The call site code itself is usually not very informative
>>
>> I start with Groovy 1.0, since that is basically the fallback path. Here
>> this.foo() translates more or less to
>> ScriptBytecodeAdapter.invokeMethod0(staticCallerClass, this,"foo")
>> which basically does this.getMetaClass().invokeMethod(staticCallerClass,
>> this, "foo"). The problem is that the meta class might be user supplied and
>> the code executed in invokeMethod as well. The invocation is then finally
>> done by reflection. That means we have frames from ScriptBytecodeAdapter,
>> from the meta class, as well as maybe frames from a custom meta class and
>> reflection frames. At the level of ScriptBytecodeAdapter there is a means of
>> transporting the caller class, but that is the static one. Once there is a
>> subclass, this information is different from what is needed here and it
>> cannot simply be exchanged. Even if the bytecode adapter is changed, we
>> cannot change the public API for MetaClass#invokeMethod now. And then the
>> information would be lost.
>>
>> In later versions of Groovy (since 1.6) we introduced a custom call site
>> caching technique, which uses runtime generated classes to create a helper
>> class per call site and is then used for invocation. At the callsite we
>> basically have something like callsiteArray[i].invoke(..). Here again the
>> staticCallerClass can be found. In this version we are able to "get" the
>> method we want to invoke, before invoking it (bypassing
>> MetaClass#invokeMethod). But to be able to get the method, certain
>> conditions have to be met (like no user supplied meta class). If they are
>> not met, then we do basically the same path as in 1.0, only that we don't
>> use ScriptBytecodeAdapter. Instead We use our CallSite class as entrance
>> point, which then makes the call to the meta class. In the "efficent" case
>> we have now frames from the callsite handling code between the callsite and
>> the target method only. This includes reflection in the first instantiation,
>> later the generated class is used so it reduces !
> to two frames of which one is the Callsite entrance point, the other a frame
> form the generated method. In the fallback case we have frames from the
> callsite handling code, plus meta class code, plus reflection of course.
> Again the fallback case prevents us from transporting the caller information
> to the target method. If we ignore the fallback case, then we could here
> maybe use the Threadlocal information. It will require a new callsite
> interface for the bytecode though, meaning this code will not work for
> precompiled grovvy of older version, excluding from getting into Groovy
> 2.1.x, since it would be a breaking change. The earliest version for that
> would be Groovy 2.2.0, which is almost in RC now. Effectively it would mean
> we would have to do a 2.3.0 very soon after most probably.
>>
>> In Groovy 2 we added an indy implementation, which replaces the callsite
>> caching code. At the callsite we have here basically invokedynamic "foo"
>> with IndyInterface#bootstrap. bootstrap will first introduce a target for
>> IndyInterface#selectMethod, since I need the runtime types instead of the
>> static ones. The static caller class information is here part of the
>> bootstrap method as Lookup object, added by invokedynamic itself. After
>> selectMethod is done we have an initial invocation using invokeExact and
>> later invocations by the handle stored in the callsite. Of course the same
>> conditions as for the callsite caching above have to be met, meaning the
>> fallback path might appear. That makes initially one IndyInterface frame,
>> then invokedynamic and lambda related frames, then optionally the traget
>> method, or in the fallback case the meta class frames plus reflection
>>
>>
>> bye Jochen
>>
>> --
>> Jochen "blackdrag" Theodorou - Groovy Project Tech Lead
>> blog: http://blackdragsview.blogspot.com/
>> german groovy discussion newsgroup: de.comp.lang.misc
>> For Groovy programming sources visit http://groovy-lang.org
>>
>
> _______________________________________________
> mlvm-dev mailing list
> [email protected]
> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev