Compiler bug: Instantiating non-static inner classes using constructor references inside lambdas

Remi Forax forax at univ-mlv.fr
Tue Nov 4 00:47:50 UTC 2014


Hi Victor, hi all,
it seems that javac forget that an inner class has a hidden argument 
that should be passed at creation time :(

First, in order to compile javac should rewrite foo like this,
it's a little more complex in reality because the second lambda can be 
loaded as a real method reference

     public void foo() {
         Boom es = new Boom();
         go(() -> es.test(boom -> this.new Crash(boom)));
     }

but the generated bytecode of foo and of the corresponding lambda seems 
very wrong,
first the code of foo,
public void foo();
     Code:
        0: new           #2                  // class MakeVerifyError$Boom
        3: dup
        4: invokespecial #3                  // Method 
MakeVerifyError$Boom."<init>":()V
        7: astore_1
        8: aload_1
        9: invokedynamic #4,  0              // InvokeDynamic 
#0:run:(LMakeVerifyError$Boom;)Ljava/lang/Runnable;
       14: invokestatic  #5                  // Method 
go:(Ljava/lang/Runnable;)V
       17: return

as you can see the problem is that the first lambda should capture 2 
arguments 'es' and 'this' but
the code capture only 'es'. javac seems to forget that to create a Crash 
you need a MakeVerifyError
because it's an inner class.

so the code should be:
        ...
        7: astore_1
        8: aload_0     // load this !
        9: aload_1
       10: invokedynamic #4,  0              // InvokeDynamic 
#0:run:(LMakeVerifyError;LMakeVerifyError$Boom;)Ljava/lang/Runnable;
       15: invokestatic  #5                  // Method 
go:(Ljava/lang/Runnable;)V
       18: return

and the code of the lambda is wrong too, instead of:
private static void lambda$foo$0(MakeVerifyError$Boom);
     Code:
        0: aload_0
        1: aload_0
        2: invokedynamic #6,  0              // InvokeDynamic 
#1:apply:(LMakeVerifyError;)Ljava/util/function/Function;
        7: invokevirtual #7                  // Method 
MakeVerifyError$Boom.test:(Ljava/util/function/Function;)V
       10: return

it should be:
      private static void 
lambda$foo$0(MakeVerifyError,MakeVerifyError$Boom);
     Code:
        0: aload_1
        1: aload_0
        2: invokedynamic #6,  0              // InvokeDynamic 
#1:apply:(LMakeVerifyError;)Ljava/util/function/Function;
        7: invokevirtual #7                  // Method 
MakeVerifyError$Boom.test:(Ljava/util/function/Function;)V
       10: return

I hope this can help.

cheers,
Rémi

On 11/03/2014 03:12 AM, Victor Williams Stafusa da Silva wrote:
> Hi, I used javac 1.8.0_25 with this code:
>
>     package com.example;
>     import java.util.function.Function;
>     public class MakeVerifyError {
>         public void foo() {
>             Boom es = new Boom();
>             go(() -> es.test(Crash::new));
>         }
>         private static void go(Runnable run) {}
>         public class Crash { public Crash(Boom e) {} }
>         public static class Boom { public void test(Function<Boom, ?>
>     ctor) {} }
>         public static void main(String[] args) {}
>     }
>
>
> And this was the result:
>
>     java.lang.VerifyError: Bad type on operand stack
>     Exception Details:
>       Location:
>     com/example/MakeVerifyError.lambda$foo$0(Lcom/example/MakeVerifyError$Boom;)V
>     @2: invokedynamic
>       Reason:
>         Type 'com/example/MakeVerifyError$Boom' (current frame,
>     stack[1]) is not assignable to 'com/example/MakeVerifyError'
>       Current Frame:
>         bci: @2
>         flags: { }
>         locals: { 'com/example/MakeVerifyError$Boom' }
>         stack: { 'com/example/MakeVerifyError$Boom',
>     'com/example/MakeVerifyError$Boom' }
>       Bytecode:
>         0x0000000: 2a2a ba00 0600 00b6 0007 b1
>     at java.lang.Class.getDeclaredMethods0(Native Method)
>     at java.lang.Class.privateGetDeclaredMethods(Class.java:2693)
>     at java.lang.Class.privateGetMethodRecursive(Class.java:3040)
>     at java.lang.Class.getMethod0(Class.java:3010)
>     at java.lang.Class.getMethod(Class.java:1776)
>     at
>     sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
>     at
>     sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
>     Exception in thread "main" Java Result: 1
>
>
> If the 'static' modifier is added to the Crash class, it works fine.
> If the 'test' method call is moved to outside the lambda, it works fine.
> If the Boom object is instantiated inside the lambda, it fails to load 
> the class:
>
>     Error: Could not find or load main class com.example.MakeVerifyError
>     Java Result: 1
>
>
> So, obviously this looks like a compiler bug.
> Do somebody already saw that before?
>
> Victor Williams Stafusa da Silva

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20141104/1caa982c/attachment-0001.html>


More information about the compiler-dev mailing list