Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8066114

javac generates incorrect invokedynamic instruction that results in an IllegalAccessError

    XMLWordPrintable

Details

    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);
        }

      Attachments

        Issue Links

          Activity

            People

              dlsmith Dan Smith
              psandoz Paul Sandoz
              Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

                Created:
                Updated: