[nestmates] javac does not desugar a protected method reference declared in super class in a different package
Mandy Chung
mandy.chung at oracle.com
Tue Oct 15 20:02:50 UTC 2019
While assessing the compatibility concern when converting
LambdaMetaFactory to hidden nestmate class, I'm getting to think whether
we should consider backporting JDK-8227415 how javac determines if a
protected method reference should be desugared to a static method, or
whether looking for an alternative solution to allow existing compiled
class to work.
Here is the example (see JDK-8227415 for details):
A simple case where p.Sub1 extends q.I (in two different packages)
and q.I::readFile is a protected method:
class Sub1 extends I {
public void test(Path outputDir) {
// the lambda proxy class generated for this::readFile method ref
// is not a subclass of q.I
test(this::readFile, outputDir);
}
private void test(Function<Path,String> func, Path path) {
func.apply(path);
}
}
p.Sub1 is a subclass of q.I whereas the lambda proxy class is not a
subclass of q.I.WhenLambdaMetaFactory is converted to define lambda
proxy class as a hidden nestmate via Lookup::defineHiddenClass,IAE would
be thrown as it fails to access q.I::readFile.
Today it happens to work because lambda proxy class is defined as VM
anonymous class which gets special access. JEP 181 defines that
nestmates allows access to private members in classes or interfaces in
the same nest but not protected members in a different package as what
VM anonymous class allows. So this javac adjustment could be made as a
follow-up of JEP 181.
Another case:
class T {
:
private static void m(Function<Path,String> func, Path path) {
func.apply(path);
}
public static class Sub extends I {
public void test(Path outputDir) {
// this method reference is desugared to a static method:
// REF_invokeSpecial p/T$Sub.lambda$test$0:(Ljava/nio/file/Path;)Ljava/lang/String;
// as Sub is in package p, a different package from its its superclass
m(super::readFile, outputDir);
// this compiles to indy LMF with:
// REF_invokeVirtual q/I.readFile:(Ljava/nio/file/Path;)Ljava/lang/String;
//
// p.T is not a subclass of q.I and so it has no access to q.I::readFile
m(this::readFile, outputDir);
}
}
}
In p.T.Sub::test, super::readFile will get desugared to a static method
in p.T.Sub to invoke q.I::readFile. However, this::readFile does not
get desugared. When p.T.m invokes such a protected method reference, p.T
has no access to q.I::readFile.
Srikanath has fixed this javac issue [1] in the nestmates branch. It's a
compatibility risk that impacts existing compiled class if such a bridge
method is missing and they will need recompiled.
A potential alternative solution that may invoke lambda proxy class to
invoke the protected method via MethodHandle by calling Lookup::in and
Lookup::findSpecial.
I don't know how high this compatibility concern is. We only hit one
JDK test failure due to this issue though. Do you consider fixing javac
causes high compatibility risk?
If not, I do think we should consider backporting JDK-8227415 to
existing releases to allow developers to prepare for the hidden classes
in advance.
What do you think?
thanks
Mandy
[1] http://hg.openjdk.java.net/valhalla/valhalla/rev/15515009b454
More information about the valhalla-dev
mailing list