JDK-8066114 : javac generates incorrect invokedynamic instruction that results in an IllegalAccessError
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 8,9
  • Priority: P4
  • Status: In Progress
  • Resolution: Unresolved
  • Submitted: 2014-11-27
  • Updated: 2018-06-15
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbd_majorUnresolved
Related Reports
Relates :  
Relates :  
Description
Consider the following code in one package

  abstract class AbstractEvent { }

  public interface AbstractListener<E extends AbstractEvent> {
      void handle(E e);
  }

  public class ConcreteEvent extends AbstractEvent {
  }

  public interface ConcreteListener extends AbstractListener<ConcreteEvent> {
  }

(Note: AbstractEvent is package private.)

And some code in another package that depends on the Concrete* types.

  public class LambdaTest {
      public static void main(String[] args) {
          ConcreteListener anon_cl = new ConcreteListener() {
              public void handle(ConcreteEvent ce) {
                  System.out.println("ANON");
              }
          };

          ConcreteListener lambda_cl = ce -> System.out.println("LAMBDA");
      }
  }

When LambdaTest is run an IllegalAccessError will be thrown when attempting to execute the invokedynamic instruction associated with the lambda expression.

Byte code for the invokedynamic will be similar to the following (package names will differ for the above classes):

    INVOKEDYNAMIC handle()Lltest/example/ConcreteListener; [
      // handle kind 0x6 : INVOKESTATIC    java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      // arguments:
      (Lltest/example/AbstractEvent;)V, 
      // handle kind 0x6 : INVOKESTATIC
      ltest/lamda/LambdaTest.lambda$main$0(Lltest/example/ConcreteEvent;)V, 
      (Lltest/example/ConcreteEvent;)V
    ]

The method signature for the SAM is "(Lltest/example/AbstractEvent;)V" which is encoded as a method type in the constant pool. When that method type is processed it results in an IllegalAccessError occurs when verifying the access to the class AbstractEvent, since it is package private and in a different package to the accessing class LambdaTest.

The anonymous inner class works fine:

  final class ltest.lamda.LambdaTest$1 implements ltest.example.ConcreteListener {
    ltest.lamda.LambdaTest$1();
    public void handle(ltest.example.ConcreteEvent);
    public void handle(ltest.example.AbstractEvent);
  }
 
The latter method is a bridge method.

The inner class spun up my the LambdaMetafactory contains no bridges:

  final class ltest.lamda.LambdaTest$$Lambda$1 implements ltest.example.ConcreteListener {
    private ltest.lamda.LambdaTest$$Lambda$1();
    public void handle(ltest.example.ConcreteEvent);
  }

Comments
The generated class *must* override the descriptor "(LAbstractEvent;)V". Otherwise, it won't properly implement the interface. To use LambdaMetafactory via invokedynamic, we have to express that descriptor with a MethodType JVM constant. Per JVMS, resolution of the MethodType performs access checks (5.4.3.5, 5.4.3.1), and will fail when attempting to resolve AbstractEvent. So our options are: - Wait for JDK-8130089 to resolve the underlying problems with MethodType and the JVM, or - Introduce an alternative LambdaMetafactory method that operates on string descriptors, not MethodTypes (with fewer restrictions on parameter/return types?), or - Reject this code with a compiler error, specified by JLS The compiler error alternative is not great, because it introduces an inconsistency between lambdas and anonymous inner classes. (How many refactoring tools would catch this, for example?)
21-01-2017

I'm currently working on other things. Feel free to assign to yourself. I would definitely want to review.
05-01-2015

Robert, I am looking for some interesting tickets to work on - unless you are actively working on this, may I take over this one ? Perhaps you can review the patch ?
05-01-2015