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

Vicente-Arturo Romero-Zaldivar vicente.romero at oracle.com
Tue Nov 4 18:14:59 UTC 2014


Hi all,

I can't reproduce this error on jdk9 repo but it's still present in 
8udev repo. Thanks for the report and for the small example. I will 
locate the related changeset and create a backport to 8udev repo.

Vicente

On 11/03/2014 04:47 PM, Remi Forax wrote:
> 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/877e47ba/attachment.html>


More information about the compiler-dev mailing list