JDK-8155803 : Method reference can emit bytecode pointing at non-visible base types
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 8
  • Priority: P4
  • Status: Resolved
  • Resolution: Duplicate
  • OS: generic
  • CPU: generic
  • Submitted: 2016-04-27
  • Updated: 2016-05-26
  • Resolved: 2016-05-19
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_92"
Java(TM) SE Runtime Environment (build 1.8.0_92-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.92-b14, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Darwin 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64

A DESCRIPTION OF THE PROBLEM :
When a method reference is created to a public type where the referenced public method is inherited from a non-visible class, the Java compiler incorrectly points the invokevirtual/invokeinterface bytecode at that non-visible base type instead of the visible subclass.

If the base type is swapped for a package-private interface which defines the public method (with a default impl), the generated bytecode will not only point at the non-visible interface type but will also be an invokeinterface.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create a package-private class (e.g., 'Base') which defines a public method (e.g., 'method'). In the same package, create public class (e.g., 'Subclass') which extends the package-private class and defines no additional methods.

In another package, specify a method reference to the public class from the first package and the public method inherited from its base class.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The generated synthetic method for the method reference contains an invokevirtual which points at the public subclass and the method name. This allows the method to be called on subclass instances like a normal method call.
ACTUAL -
An IllegalAccessError is thrown because the invokevirtual instruction points at the non-visible base class which is package-private in a different package.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.IllegalAccessError: tried to access class com.example.sub.Base from class com.example.Main
	at com.example.Main.lambda$main$0(Main.java:10)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
// Subclass.java
package com.example.sub;

abstract class Base {
  public String method() {
    return "Hello!";
  }
}

public class Subclass extends Base {
}

// Main.java
package com.example;

import com.example.sub.Subclass;
import java.util.Collections;

public final class Main {
  public static void main(String... args) {
    Collections.singleton(new Subclass())
        .stream()
        .map(Subclass::method)
        .forEach(System.out::println);
  }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Switch the method reference to a lambda.

  .map(Subclass::method)

becomes

  .map(o -> o.method())


Comments
8uxx - Fail 9 ea b-115 - Pass
02-05-2016

This issue exist in 8 and works fine in 9 ea b115 == -sh-4.1$ /opt/java/jdk1.8.0_92/bin/java com.example.Main Exception in thread "main" java.lang.IllegalAccessError: tried to access class com.example.sub.Base from class com.example.Main at com.example.Main.lambda$main$0(Main.java:10) at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) at java.util.Collections$2.tryAdvance(Collections.java:4717) at java.util.Collections$2.forEachRemaining(Collections.java:4725) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418) at com.example.Main.main(Main.java:11) -sh-4.1$ /opt/java/jdk1.8.0/bin/javac com/example/Main.java com/example/sub/Subclass.java -sh-4.1$ /opt/java/jdk1.8.0/bin/java com.example.Main Exception in thread "main" java.lang.IllegalAccessError: tried to access class com.example.sub.Base from class com.example.Main at com.example.Main.lambda$MR$main$method$1a0824df$1(Main.java:10) at com.example.Main$$Lambda$1/1995265320.apply(Unknown Source) at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) at java.util.Collections$2.tryAdvance(Collections.java:4776) at java.util.Collections$2.forEachRemaining(Collections.java:4784) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502) at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418) at com.example.Main.main(Main.java:11) -sh-4.1$ /opt/java/jdk-9_ea-11 com/example/Main.java com/example/sub/Subclass.java jdk-9_ea-110/ jdk-9_ea-115/ -sh-4.1$ /opt/java/jdk-9_ea-115/bin/javac com/example/Main.java com/example/sub/Subclass.java -sh-4.1$ /opt/java/jdk-9_ea-11 com.example.Main jdk-9_ea-110/ jdk-9_ea-115/ -sh-4.1$ /opt/java/jdk-9_ea-115/bin/java com.example.Main Hello! ==
02-05-2016