The second form - (.foo bar), expanded to (. bar foo) - eventually
calls Reflector.invokeNoArgInstanceMember.
The first form - (.foo (new Bar)), expanded to (. (new Bar) foo) -
doesn't seem to use any Reflector _invocation_ methods. I also added a
breakpoint to java.lang.reflect.Method.invoke() and it never hits that
breakpoint.
It seems that Reflector.getMethods is actually used to resolve Methods
for placement in the object expression "ObjExpr fexpr" on line 5421 of
Compiler.java. After that poing I'm unable to trace where the actual
invocation takes place.
I am trying to understand how the first form gets evaluated because I
have a case where JRebel instrumentation causes this to work:
(def bar (new foo.core.Bar))
#'user/bar
(.sayAhoj bar)
"Ahoj!"
but this to fail:
(.sayAhoj (new foo.core.Bar))
java.lang.NoSuchMethodError: foo.core.Bar.sayAhojLjava/lang/String;
(NO_SOURCE_FILE:0)
even though the method appears in the stub and can be invoked fine via
reflection. More information here:
http://blog.robert-campbell.com/post/2760935713/clojures-gen-class-and-jrebel
For that form, the clojure compiler cannot infer the type of bar, and
does not know which exact method (class + type signature) .foo
represents. Due to this, a runtime lookup that uses the reflector is
inserted, since a method invocation in java byte code must be
statically typed (at least currently).
> The first form - (.foo (new Bar)), expanded to (. (new Bar) foo) -
> doesn't seem to use any Reflector _invocation_ methods. I also added a
> breakpoint to java.lang.reflect.Method.invoke() and it never hits that
> breakpoint.
This is because the compiler knows the type of the arg to .foo, and
can emit bytecode for a plain old (statically typed) method
invocation.
Since reflective method invocations are pretty slow, you often try to
add type hints so that it can be avoided: (.foo ^Bar bar) The
generated bytecode for this method invocation should be very similar
(if not identical) to the bytecode for (.foo (new Bar))
// raek
So method invocation via reflection works, while invokevirtual does
not. As a sanity check I compared the bytecode of an AOT'd (working)
invocation of a method compiled and loaded before the JRebel reload
with a failing invocation after the reload. As expected they both
contain the exact same code:
71c71
< 10: invokevirtual #32; //Method foo/core/Bar.sayAhoj:()Ljava/lang/String;
---
> 10: invokevirtual #32; //Method
foo/core/Bar.sayHello:()Ljava/lang/String;
The only difference I've been able to spot so far between the vanilla
method invocations (pre JRebel reloading) and the freshly added and
reloaded method invocations is in the object expression:
InstanceMethodExpr for sayAhoj (newly reloaded JRebel, broken):
method.clazz = class foo.core.Bar$$M$8e38ee87
method.parameterTypes = java.lang.Class[1], [0] = [class foo.core.Bar]
InstanceMethodExpr for sayHello (loaded initially, working):
method.clazz = class foo.core.Bar
method.parameterTypes = java.lang.Class[0]
This mostly makes sense since I'm assuming JRebel has to load a new
class and perform some behind the scenes magic to write it up to look
like the original. The extra parameter looks like the [this] from the
function. I tried setting these values to match the sayHello versions,
but it didn't help.
Those differences don't seem to actually matter since at the end of
the day the compiled bytecode is twi identical invokevirtuals.
Rob
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en