On Jul 30, 2013, at 4:11 PM, Nick Williams <nicholas+open...@nicholaswilliams.net> 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 > mlvm-dev@openjdk.java.net > http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev _______________________________________________ mlvm-dev mailing list mlvm-dev@openjdk.java.net http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev