X

Sundararajan's Weblog

  • Java
    September 1, 2006

invokespecialdynamic?

Guest Author
There are 4 JVM bytecodes to call methods:
  1. invokeinterface - used to call an interface method on an object
  2. invokestatic - used to call a static method of a class
  3. invokevirtual - used to call a overridable method
  4. invokespecial - used to call
    • constructors
    • private instance methods
    • super class methods (super.foo() calls in the source)


All these instructions require the specification of
  1. target class (or interface for invokeinterface) name
  2. the name of the method (or <init> for constructors)
  3. the signature of the method.

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());

Problem with calling super class methods

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():


  1. Look for "foo" in super class or it's super class and so on.
    If found, generate invokespecial
  2. if matching method is not found in super class(es), instead of
    throwing error, call the 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?

What would the invokespecialdynamic do?

invokespecialdynamic would be like invokespecial
except that


  1. it won't accept the method signature (handle overloading in the
    same way as invokedynamic would do!)
  2. if the method of specified name is found at runtime, fallback
    step is taken (that could be same as invokedynamic
    -like throwing exception or calling a special method)

With invokespecialdynamic, Groovy would be able to flexibly implement
super.foo() calls.

Join the discussion

Comments ( 6 )
  • Charles Oliver Nutter Friday, September 1, 2006
    Very interesting. I took a few moments to try to poke a hole in your logic and was unable to come up with anything compelling. invokedynamic can't be dual-purpose, because calling invokedynamic against the current object would always just be a recursion. invokespecial can't be dual-purpose because of the requirement for a type and signature. Hmm.

    Perhaps a less cumbersome name might be invokesuper. I am not familiar with the original decisions that called for invokespecial to call private methods, but it may not be necessary when considering dynamic invocation. If you want to do a super call from within a dynamic method, you will generate an invokesuper. This would mimic how most dynamic languages handle super calls...they are a unique node in the AST or a bytecode in the machine. So generally there's two types of calls that are dynamic:

    - dynamically calling method X against a given object, and

    - calling super method X from within the original method X

    invokedynamic and invokesuper. This is no different than what you describe, however, so I guess I'm still agreeing with you :)

  • John Wilson Saturday, September 2, 2006
    The Groovy method invocation mechanism doesn't work in quite the way you describe. The calls always go via the object's MetaClass. Each groovy object has a reference to its MetaClass instance and every Java object's MetaClass can be found by looking it up in the MetaClass registry. Groovy objects can have their MetaClass changed on a per instance basis, all Java instances of the same class have the same MetaClass.

    The MetaClass determines the behaviour of the object. The properties, fields and methods on the object are generally accessible via the MetaClass but if you give an object a custom MetaClass you can add, remove and alter methods, fields and properties. The standard metaClass can alter its behaviour dynamically to allow the implementation of Smalltalk stlye categories (the fact that groovy supports multi threaded execution makes this quite an interesting thing to implement!) The MetaClass is fundamental to the semantics of the language. At the moment the MetaClass sometime uses reflection and sometimes does dynamic bytecode generation but that's just an implementation detail (though the performance differences are interesting and might be the subject of a different discussion!)

    The MetaClass implements our Meta Object Protocol (MOP). The invokeMethod, etc. methods on Groovy objects is just a convenience feature to allow the programmer to add dynamic behaviour without having to write and register a custom MetaClass. At the moment our MOP is not powerful enough to express all the semantics wee need. We have designed and are implementing a new MOP which, we believe, will fix this problem. So we are adding invokeThisMethod and invokeSuperMethod to the existing invokeMethod functions on the MetaClass. so:

    x.foo() -> x.getMetaClass().invokeMethod(x, "foo", new Objetc[]{})

    this.foo() -> this.getMetaClass().invokeThisMethod(this, "foo", new Objetc[]{})

    super.foo() -> this.getMetaClass().invokeSuperMethod(this, "foo", new Objetc[]{})

    The implementation of invokeSuperMethod is hard!

    As far as we can see there is no way in the JVM for one class to invoke a super method on another class. There may be a security issue which means that this behaviour is dangerous but we can't think of one. At the moment we have to generate synthetic method on each class to expose the methods which could be called via super. This is not an ideal solution but is the best we can do.

    It look as if invokespecialdynamic might help us here. We will follow the progress of this with interest. We would be happy to work with you all to make sure that groovy can benefit from your work.

  • A. Sundararajan Saturday, September 2, 2006
    Hi John Wilson: Yes, I agree that my description of Groovy's MOP is simplistic and that was intentional. I did read the sources of InvokerHelper/Invoker/MetaClass/MetaClassImpl/ProxyMetaClass etc. of Groovy [may be that is a topic for another blog entry or series of entries]. I wanted to get the central point across - which as you said: implementation of invokeSuperMethod-like mechanism is hard with current VM. Besides the synthetic methods can not be generated by pre-compiled classes - for example JDK platform classes (unless we are willing to do load-time transformation of "the whole world"). But, I do agree that synthetic methods seems to be the best that can be done as of now.
  • Jochen Theodorou Monday, September 4, 2006
    I guess to get invokedynamic working, as Groovy might need it, it might be good to have the ability to give a partial signature of the method we want to call. The runtime type is not always

    non-ambiguous when invoking a method. Part of this signature could be the class we want to make the call on. A missing entry in the signature would mean to use the runtime type. So if we can put the type of "this" in there as part of the call signature, then we have no problem. But I am unsure if having a bigger String and instruction code is what the VM guys are after ;)


    And one more thing.... Groovy can use both, Reflection and generated classes to make a method call. I would very much like to compare them. Do you have an idea of what kind of test would be suitable for this? Spontaneously I am thinking of something ackermann based, but maybe you have a better idea?
  • A. Sundararajan Monday, September 4, 2006
    Hi Jochen Theodorou: Sun's JDK generates speed-up bytecodes for reflective access only if the reflective Method becomes "hot" (much like the hotspot's bytecode-to-machine-code compilation). So, you may have to pre-heat the "Method" object by few invokes and then compare the numbers b/w direct invocation and Method.invoke(). But, I am sorry - I don't have any specific benchmark suggestion.
  • jochen Theodorou Tuesday, September 5, 2006
    Hi again. I made some tests and it seems the reflection modell is as fast as the reflector model Groovy does use. Not really a wonder, they both generated classes. But most of the time is not used make the method call itself! Most of the time is used to make an Object[]. A method taking 2 arguments and returning an ordinary number is about 3 times slower than a method with the same logic but taking no argument. I see possibilities to go around that using the Reflector model, but I see no way for the Reflection model here. invokedynamic might speed up all this very much, if it helps to avoid to create an Object[]. Maybe we should experiment with reuseable Object[], but I am not sure this will be really of much help.
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.