Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 bytecode how are default methods in interfaces marked

Tags:

java

bytecode

since Java 8 it is allowed to predefine methods in interfaces. Some of these standard implementations are already implemented on the "standard" interfaces such as CharSequence. If you try to read Java 8 ByteCode (e.g. the rt.jar of the Java home folder) with a JVM 7, an error occurs.

e.g. the type java.lang.CharSequence cannot be resolved.

This may not be the only difference between levels, but I would like to understand the structure of the new byte code.

public interface com.company.ITest {
public void HelloWorld();
Code:
   0: getstatic     #1                  // Field java/lang/System.out:Ljava/io/PrintStream;
   3: ldc           #2                  // String hello world
   5: invokevirtual #3                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8: return
}

This Bbytecode was produced by this Javacode:

public interface ITest {
    default void HelloWorld(){
        System.out.println("hello world");
    }
}

So here is my question: How does an default interface impact on the constant pool?

Is any of these flags relevant:

CONSTANT_MethodHandle CONSTANT_MethodType CONSTANT_InvokeDynamic

like image 793
osanger Avatar asked Mar 31 '18 08:03

osanger


2 Answers

Take following example:

public interface A {
    default void foo(){
       System.out.println("Calling A.foo()");
    }
}

public class Clazz implements A {
}

From the client code perspective, default methods are just ordinary virtual methods. Hence the name – virtual extension methods. So in case of the example with the client code that invokes the default method will generate invokeinterface at the call site.

A clazz = new Clazz();
clazz.foo(); // invokeinterface foo()

Clazz clazz = new Clazz();
clazz.foo(); // invokevirtual foo()

In case of the default methods conflict resolution, when we override the default method and would like to delegate the invocation to one of the interfaces the invokespecial is inferred as we would call the implementation specifically:

public class Clazz implements A, B {
    public void foo(){
       A.super.foo(); // invokespecial foo()
    }
}

public void foo();
Code:
0: aload_0
1: invokespecial #2 // InterfaceMethod A.foo:()V
4: return

As you can see, invokespecial instruction is used to invoke the interface method foo(). This is also something new from the bytecode point of view as previously you would only invoke methods via super that points to a class (parent class), and not to an interface.

like image 101
Anurag Sharma Avatar answered Oct 16 '22 02:10

Anurag Sharma


It depends on what you are actually doing, which obstacle you will stumble upon. You say

If you try to read Java 8 ByteCode (e.g. the rt.jar of the Java home folder) with a JVM 7, an error occurs.

e.g. the type java.lang.CharSequence cannot be resolved.

but this doesn’t even remotely match what will happen when you try to “read Java 8 ByteCode with a JVM 7”. If you try to load a Java 8 class with a Java 7 JVM, you usually get a VerifyError with a message telling you that the class file version is not supported.

Instead, the error message looks very much like the well known error message of the Eclipse compiler when it fails at reading the byte code of a class file, which has nothing to do with the JVM. As explained in this answer, Eclipse doesn’t seem to make a difference between classes it didn’t find or classes it failed to parse, just saying “cannot be resolved”. It also seems to ignore the class file version number, so it happens to work with newer class files when they are not using newer features like default methods.

On a technical level, the differences are tiny. There is no relationship between default methods and the constant pool. The pool entry types CONSTANT_MethodHandle, CONSTANT_MethodType, and CONSTANT_InvokeDynamic are not related to default methods and they are not even new—they are already part of the standard since Java 7 (which doesn’t stop some tool vendors from ignoring them as long as they do not encounter them). A default method simply is a method which is not abstract and not static, just like an ordinary public instance method, but in an interface. The new thing is that this is allowed now. A simple class file parser which doesn’t care whether it is reading a class or interface would have no difficulties with that. But depending on what the tool does with the data, it may struggle with such a class file (if it didn’t already stop at the version number), like older Eclipse compilers do.

If a JVM’s verifier didn’t already stop at the version number, it would simply throw a different VerifierError saying the the class file violates the constraint that all methods must be abstract.

like image 42
Holger Avatar answered Oct 16 '22 03:10

Holger