[PATCH] 8129740: Runtime exception when passing lambda in inner class constructor

bsrbnd bsrbnd at gmail.com
Thu Oct 8 09:15:08 UTC 2015


Any comment about this issue and the patch below?
Thanks,

bsrbnd

2015-09-17 13:46 GMT+02:00 bsrbnd <bsrbnd at gmail.com>:
> Hi,
>
> Just two precisions:
>
> 1) If you run the compiled example, a "java.lang.VerifyError
> (uninitializedThis)" is thrown, not a runtime exception (sorry for
> that mistake):
>
> Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
> Exception Details:
>   Location:
>     Bug$Inner.<init>(LBug;)V @3: invokedynamic
>   Reason:
>     Type uninitializedThis (current frame, stack[2]) is not assignable
> to 'Bug$Inner'
>
>
> 2) The output of javap on "Inner" class gives us a good explaination.
> The lambda expression is translated into an instance method (to access
> instance members of the class and outer-class) and thus "this" is used
> by "invokedynamic" before calling the constructor which leads to the
> VerifiyError:
>
> public class Bug$Inner {
>   java.lang.Object vi;
>   final Bug this$0;
>   public Bug$Inner(Bug, java.lang.Runnable);
>   public Bug$Inner(Bug, java.lang.Object);
>   public Bug$Inner(Bug);
>   private void lambda$new$0();
> }
>
>   public Bug$Inner(Bug);
>     Code:
>        0: aload_0
>        1: aload_1
>        2: aload_0
>        3: invokedynamic #8,  0              // InvokeDynamic
> #0:run:(LBug$Inner;)Ljava/lang/Runnable;
>        8: invokespecial #9                  // Method
> "<init>":(LBug;Ljava/lang/Runnable;)V
>       11: return
>
> If the lambda expression doesn't access instance members of the class
> (but static members or instance members of other classes), the lamda
> expression is translated into a static method an the "invokedynamic"
> doesn't need "this". For example:
>  this(()->System.out.println(new Other().o));
>
> gives:
>
> public class Bug$Inner {
>   java.lang.Object vi;
>   final Bug this$0;
>   public Bug$Inner(Bug, java.lang.Runnable);
>   public Bug$Inner(Bug, java.lang.Object);
>   public Bug$Inner(Bug);
>   private static void lambda$new$0();
> }
>
>   public Bug$Inner(Bug);
>     Code:
>        0: aload_0
>        1: aload_1
>        2: invokedynamic #8,  0              // InvokeDynamic
> #0:run:()Ljava/lang/Runnable;
>        7: invokespecial #9                  // Method
> "<init>":(LBug;Ljava/lang/Runnable;)V
>       10: return
>
> Thus, jls 8.8.7.1 seems to be respected (the error appears to be
> "right" in issue 8129740) and then javac should (I think) give an
> error message as shown in the patch below.
>
> Regards,
> bsrbnd
>
> 2015-09-14 15:11 GMT+02:00 bsrbnd <bsrbnd at gmail.com>:
>> Hi,
>>
>> As explained in issue 8129740, the following code produces a runtime
>> exception ("uninitialized this"):
>>
>> public class Bug {
>>     Object vb=new Object();
>>
>>     public class Inner {
>>         Object vi=new Object();
>>
>>         public Inner(Runnable r) {r.run();}
>>         public Inner(Object o) {System.out.println(o);}
>>         public Inner() {
>>             this(()->System.out.println(vb)); // KO (bug 8129740
>> runtime exception)
>>         }
>>     }
>>     public static void main(String[] args) {
>>         new Bug().new Inner();
>>     }
>> }
>>
>> class Other {
>>     public Object o=new Object();
>> }
>>
>> With respect to JLS 8.8.7.1 (p. 264, l. 1-4), I think (if I've not
>> missed anything) that this example should probably run without
>> exception because "vb" is not within "Inner" class but in the
>> outer-class.
>>
>> For example, all the following statements compile and run according to
>> jls 8.8.7.1:
>>  this(vb);
>>  this(()->System.out.println(new Other().o));
>>  this(new Runnable() {public void run() {System.out.println(vb);}});
>>
>> And the following ones doesn't compile, according to jls:
>>  this(vi);
>>  this(()->System.out.println(vi));
>>  this(new Runnable() {public void run() {System.out.println(vi);}});
>>
>> But, if the runtime exception is "right" (as said in issue 8129740),
>> we can detect in the attribute phase the special case of an outer
>> member access within a lambda expression in a this/super call as shown
>> in the following patch.
>> Method Attr.visitLambda() keeps a track in the AttrContext if we are
>> in a this/super call and method Attr.visitIdent() produces an error
>> message for that case (notice that "noOuterThisPath" on line 3209
>> never changes).
>>
>> Regards,
>> bsrbnd
>>
>> diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
>> b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
>> --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
>> +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
>> @@ -2327,6 +2327,7 @@
>>          }
>>          //create an environment for attribution of the lambda expression
>>          final Env<AttrContext> localEnv = lambdaEnv(that, env);
>> +        localEnv.info.isLambdaInSelfCall = localEnv.info.isSelfCall;
>>          boolean needsRecovery =
>>                  resultInfo.checkContext.deferredAttrContext().mode ==
>> DeferredAttr.AttrMode.CHECK;
>>          try {
>> @@ -3207,6 +3208,7 @@
>>          //     (`noOuterThisPath').
>>          Env<AttrContext> symEnv = env;
>>          boolean noOuterThisPath = false;
>> +        boolean isLambdaInSelfCall = symEnv.info.isLambdaInSelfCall;
>>          if (env.enclClass.sym.owner.kind != PCK && // we are in an inner class
>>              sym.kind.matches(KindSelector.VAL_MTH) &&
>>              sym.owner.kind == TYP &&
>> @@ -3238,7 +3240,7 @@
>>          // In a constructor body,
>>          // if symbol is a field or instance method, check that it is
>>          // not accessed before the supertype constructor is called.
>> -        if ((symEnv.info.isSelfCall || noOuterThisPath) &&
>> +        if ((symEnv.info.isSelfCall || isLambdaInSelfCall ||
>> noOuterThisPath) &&
>>              sym.kind.matches(KindSelector.VAL_MTH) &&
>>              sym.owner.kind == TYP &&
>>              (sym.flags() & STATIC) == 0) {
>> diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java
>> b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java
>> --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java
>> +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java
>> @@ -72,6 +72,8 @@
>>       *  Is this an attribution environment for an instance creation expression?
>>       */
>>      boolean isNewClass = false;
>> +
>> +    boolean isLambdaInSelfCall = false;
>>
>>      /** Are arguments to current function applications boxed into an
>> array for varargs?
>>       */
>> @@ -112,6 +114,7 @@
>>          info.isSpeculative = isSpeculative;
>>          info.isAnonymousDiamond = isAnonymousDiamond;
>>          info.isNewClass = isNewClass;
>> +        info.isLambdaInSelfCall = isLambdaInSelfCall;
>>          return info;
>>      }


More information about the compiler-dev mailing list