There are 4 JVM bytecodes to call methods:
invokeinterface - used to call an interface method on an object
invokestatic - used to call a static method of a class
invokevirtual - used to call a overridable method
invokespecial - used to call
Please refer to the JVM specification for further details.
To facilitate the compilation of dynamically typed languages such as Groovy, JRuby, Jython to JVM bytecodes, a new bytecode called invokedynamic is being proposed (JSR-292). Note that the details of this JSR will be available only in future. Whatever I say below could be (or could become) completely wrong!
invokedynamic will (probably) not require the target class name and the
method signature. invokedynamic will search the specified method on the
target object based using just the name. The way to handle method overloading
will have to be specified by JSR]. How to handle failure to find required method?
- again this will also need to be specified by the JSR (For example, throw NoSuchMethodException or
call a pre-defined doesNotUnderstand or handleMethodCall method).
Let us consider the case of Groovy.
(JSR-241). Groovy supports
a flexible method dispatching framework. Without going into details:
if a method to be called is found in the class, then a special invokeMethod
method is called. I wrote about that in an
earlier post. Groovy would be able to make
use of invokedynamic to speed-up it's Meta Object Protocol
implementation.
class Person {
public Object invokeMethod(String methodName, Object args) {
System.out.println("calling " + methodName);
return methodName.toUpperCase();
}
};
Person b = new Person();
// This works. Calls "invokeMethod" because there is no "name"
// method in Person class.
System.out.println(b.name());
While Groovy's method dispatch is flexible for "clients" of classes,
calling a super class method is not so flexible. For example, when
super.foo() is called from a Groovy class, it is compiled
much like Java - i.e., using invokespecial. So, method
searched has to be found at "compile time". This means the following
will not work.
class Person {
public Object invokeMethod(String methodName, Object args) {
System.out.println("calling " + methodName);
return methodName.toUpperCase();
}
};
class Employee extends Person {
public String name() {
// This does *not* work! there is no "name" method in Person!
// Ideally, one would expect super.name() to call Person.invokeMethod().
return "Employee " + super.name();
}
}
We get the following "compile time" error:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed,
General error during class generation: No such method: name for class: Employee.
Node: org.codehaus.groovy.ast.expr.MethodCallExpression. At [11:40] t.groovy
1 error
A more flexible language like Smalltalk will allow the user
to call super class methods even not found during compilation. If the method
is not found in super class at runtime (or it's super class and so on), then doesNotUnderstand
method will be called [or should I say, doesNotUnderstand
message will be sent!]
But, because Groovy is limited by JVM can do, currently it uses either Java reflection or bytecode generation to implement it's meta object protocol. Java reflection does not allow us to call a overriden super class method on an object. java.lang.reflect.Method.invoke's javadoc says: If the underlying method is an instance method, it is invoked using dynamic method lookup as documented in The Java Language Specification, Second Edition, section 15.12.4.4; in particular, overriding based on the runtime type of the target object will occur.
Similarly, the reflection speed-up classes generated by Groovy can not call super class methods. [I am not sure why Groovy folks do this reflection speed-up -- HotSpot JVM already speeds up reflective calls by bytecode generation internally!]
The only way to call super class method is to generate invokespecial
which can only be inside a subclass! (and not in an unrelated reflection "speed-up" class). This explains why Groovy has to translate super.foo() with
invokespecial instruction. Groovy could probably do
the following to compile super.foo():
invokespecial
invokeMethod method.
But the problem here is that the above scheme "fixes" method dispatching
during compilation. For example, if a super class introduced "foo" method
after the compilation of subclass then that new method won't be called! (invokeMethod would be called. So, do we probably need invokespecialdynamic?
invokespecialdynamic would be like invokespecial
except that
invokedynamic would do!)
invokedynamic
-like throwing exception or calling a special method)
With invokespecialdynamic, Groovy would be able to flexibly implement
super.foo() calls.
I accidentally deleted my Jython installation. I attempted to reinstall Jython 2.1). Last time I had installed it with JDK 1.5.0_06. This time I attempted with JDK 6. I got the following error:
D:\downloads\Java\Jython>d:\jdk1.6.0\bin\java jython_21
Exception in thread "main" java.lang.ClassFormatError: Extra bytes at the end of class file jython_21
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:620)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
at java.net.URLClassLoader.access$000(URLClassLoader.java:56)
at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:276)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
Looks like Jython folks are self-extracing using the installer (jython_21.class). The installer class has extra bytes at the end of the .class file (which persumably has the installation data in compressed form).
JVM spec second edition section 4.9.1 makes it clear - a .class files can not have extra bytes at the end. May be the .class is verified with JDK 6 - but not with JDK 1.5.0_06? In fact, when using this command
D:\jdk1.5.0_06\bin\java -Xverify:all jython_21
I got the same aforementioned error - so clearly, with bytecode verification 1.5.0_06 also throws the same error. I tried the following command line:
D:\jdk1.6.0\bin\java -Xverify:none jython_21
Guess what? I got the same error! So, it appears that JDK 6's does not allow extra bytes at the end of .class files even when not doing bytecode verification!