sun.reflect.Reflection.getCallerClass(int) is going to be removed... how to replace?

Jochen Theodorou blackdrag at gmx.org
Wed Jul 3 01:50:14 PDT 2013


Am 02.07.2013 20:13, schrieb John Rose:
[...]
> The upshot of all this is that if you encapsulate your CS method calls
> in method handles obtained from a BSM or MethodHandles.lookup, your code
> won't be sensitive to the dynamic context of those calls.

Your are of course right here, but you miss one important part, because 
you limit it to direct reflection and direct invokedynamic.

Let me make an example:

> class Foo {}
> Foo.metaClass.bar = {throw new Exception("buh")}
> def foo = new Foo()
> foo.bar()

This code adds a method bar to the class Foo. To show the involved trace 
I made it throw an exception. We can assume, that I for example call 
Class#forName(String) here or something similar. The first call is done 
using reflection and the trace to the caller (where the exception is 
thrown) looks like this:

> 	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
> 	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
> 	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
> 	at java.lang.reflect.Constructor.newInstance(Constructor.java:399)
> 	at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:77)
> 	at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:102)
> 	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:57)
> 	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182)
> 	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:190)
> 	at test$_run_closure1.doCall(test.groovy:1166)

As I understand the new implementation the first 4 frames would be 
ignored, but that still leaves 5 more in the way here. An if you think 
just doing this with indy will solve the problem... no:

> 	at java.lang.invoke.MethodHandleImpl$GuardWithCatch.invoke_L2(MethodHandleImpl.java:1130)
> 	at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:212)
> 	at test$_run_closure1.doCall(test.groovy:1166)

since I need the dynamic types for a first call, I have one additional 
frame in here, which will be in the way and give me the wrong class loader.

With our callsite caching, if we look not on the first call and say we 
call a method bar statically defined in Foo, the trace to the caller 
would look like this:

> 	at Foo.bar(test.groovy:1167)
> 	at Foo$bar.call(Unknown Source)
> 	at test$_run_closure1.doCall(test.groovy:1171)

the unknown source here is the runtime generated class. The defining 
loader of that class is a child of the defining loader of the caller 
(here test). So this can work... sometimes. For indy, there will be a 
java.lang.invoke.MethodHandleImpl$GuardWithCatch.invoke_L1 in between, 
so I assume it will work there. But the first call will fail in both cases.

And for Groovy 1.0 it looks even worse.

Of course we have to do tricks to get around the limitations. In the 
past we could for example "replace" the method. Instead of using 
Class#forName(String) directly we have our own Class#forName, which then 
calls the bigger variant where I can give in a ClassLoader. We do that 
for example for ResourceBundle#getBundle(String)

But with the new logic I see no other way, then to throw an exception 
and walk the trace.

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



More information about the mlvm-dev mailing list