Miguel Gamboa
Miguel Gamboa

Reputation: 9383

How is stored the result of the invokedynamic?

Java 8 introduces support for first-class functions, which allows assigning functions to variables. In this case the variables must be of a function type, which is defined by a functional interface (an interface with just one abstract method).

So, considering an example of an interface I and a class A with the following definition:

interface I{ int foo(); }
class A implements I{ 
  public int foo(){return 7;} 
  public static int bar(){return 11;}
}

We can assign to a variable of type I an instance of A or a method reference to the method bar of A. Both can be store on variables of type I, such as:

I i1 = new A(); 
I i2 = A::bar;

If we analyze the bytecodes resulting from the compilation of the previous code we will get:

0: new           #2                  // class A
3: dup
4: invokespecial #3                  // Method A."<init>":()V
7: astore_1
8: invokedynamic #4,  0              // InvokeDynamic #0:foo:()LI;
13: astore_2

For i1 = new A(); that is clearly that the corresponding instruction 7: astore_1 is storing an instance of A that is compatible with I. But, as a result of the i2 = A::bar we are storing the result of the 8: invokedynamic #4, 0.

So, that means that the result of an invokedynamic is always an instance of the target type, which is the type of the variable that we are assigning with a method reference?

Upvotes: 2

Views: 198

Answers (2)

Holger
Holger

Reputation: 298409

The result of the invokedynamic instruction, the way Java 8’s lambda expressions and method references use it, is indeed an instance of the target functional interface.

It isn’t the result of the invokedynamic instruction that is remembered by the JVM but the CallSite that is returned by the bootstrap method, in case of the new Java 8 features one of the two methods of the LambdaMetafactory.

The CallSite instances linked to an invokedynamic instruction encapsulate behavior, not a particular result value. The actual behavior provided by the LambdaMetafactory is intentionally unspecified to provide a wide degree of freedom but the current implementation exhibits two different behaviors.

For non-capturing lambda expressions, the behavior will be to return a single instance which has been created during the invokedynamic bootstrapping. This can be done by creating a constant wrapping MethodHandle wrapped in a ConstantCallSite. In this case, subsequent executions of the invokedynamic instruction will evaluate to this instance.

For lambda expressions which capture values, the instruction will be linked against a constructor or factory method of a generated class which accepts the captured values. So then, subsequent executions of the invokedynamic instruction will behave like an ordinary object construction (which creates a new instance of the class which implements the target interface each time).

Upvotes: 4

apangin
apangin

Reputation: 98505

Each invokedynamic bytecode refers to a corresponding CONSTANT_InvokeDynamic_info structure in the constant pool. This structure contains a Method Descriptor that is used to derive the types of the arguments and the type of return value for this invokedynamic instruction.

In your example the method descriptor is ()LI; computed during source-to-bytecode translation.

8: invokedynamic #4,  0              // InvokeDynamic #0:foo:()LI;
                                                             ^^^^^

It means that this particular bytecode expects no arguments and always produces the result of type I.

Upvotes: 6

Related Questions