8071453: Allow interface methods to be private

Co-authored-by: Maurizio Cimadamore <maurizio.cimadamore@oracle.com>
Reviewed-by: mcimadamore
This commit is contained in:
Srikanth Adayapalam 2015-03-02 10:41:08 +05:30
parent 7b2bf7805a
commit 105275fb87
23 changed files with 379 additions and 22 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -299,7 +299,7 @@ public class Flags {
public static final long
ExtendedStandardFlags = (long)StandardFlags | DEFAULT,
ModifierFlags = ((long)StandardFlags & ~INTERFACE) | DEFAULT,
InterfaceMethodMask = ABSTRACT | STATIC | PUBLIC | STRICTFP | DEFAULT,
InterfaceMethodMask = ABSTRACT | PRIVATE | STATIC | PUBLIC | STRICTFP | DEFAULT,
AnnotationTypeElementMask = ABSTRACT | PUBLIC,
LocalVarFlags = FINAL | PARAMETER,
ReceiverParamFlags = PARAMETER;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -212,6 +212,7 @@ public enum Source {
public boolean allowUnderscoreIdentifier() {
return compareTo(JDK1_8) <= 0;
}
public boolean allowPrivateInterfaceMethods() { return compareTo(JDK1_9) >= 0; }
public static SourceVersion toSourceVersion(Source source) {
switch(source) {
case JDK1_2:

View File

@ -1056,9 +1056,9 @@ public class Check {
if ((sym.owner.flags_field & ANNOTATION) != 0) {
mask = AnnotationTypeElementMask;
implicit = PUBLIC | ABSTRACT;
} else if ((flags & (DEFAULT | STATIC)) != 0) {
} else if ((flags & (DEFAULT | STATIC | PRIVATE)) != 0) {
mask = InterfaceMethodMask;
implicit = PUBLIC;
implicit = (flags & PRIVATE) != 0 ? 0 : PUBLIC;
if ((flags & DEFAULT) != 0) {
implicit |= ABSTRACT;
}
@ -1128,7 +1128,7 @@ public class Check {
PRIVATE | STATIC | DEFAULT))
&&
checkDisjoint(pos, flags,
STATIC,
STATIC | PRIVATE,
DEFAULT)
&&
checkDisjoint(pos, flags,
@ -1623,8 +1623,7 @@ public class Check {
}
// Error if overriding method has weaker access (JLS 8.4.6.3).
if ((origin.flags() & INTERFACE) == 0 &&
protection(m.flags()) > protection(other.flags())) {
if (protection(m.flags()) > protection(other.flags())) {
log.error(TreeInfo.diagnosticPositionFor(m, tree), "override.weaker.access",
cannotOverride(m, other),
(other.flags() & AccessFlags) == 0 ?

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -1037,7 +1037,7 @@ public class Lower extends TreeTranslator {
MethodSymbol accessor = accessors[acode];
if (accessor == null) {
accessor = new MethodSymbol(
STATIC | SYNTHETIC,
STATIC | SYNTHETIC | (accOwner.isInterface() ? PUBLIC : 0),
accessName(anum.intValue(), acode),
new MethodType(argtypes, restype, thrown, syms.methodClass),
accOwner);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -32,6 +32,7 @@ import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.parser.Tokens.*;
import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
import com.sun.tools.javac.resources.CompilerProperties;
import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.util.*;
@ -158,6 +159,7 @@ public class JavacParser implements Parser {
this.allowTypeAnnotations = source.allowTypeAnnotations();
this.allowAnnotationsAfterTypeParams = source.allowAnnotationsAfterTypeParams();
this.allowUnderscoreIdentifier = source.allowUnderscoreIdentifier();
this.allowPrivateInterfaceMethods = source.allowPrivateInterfaceMethods();
this.keepDocComments = keepDocComments;
docComments = newDocCommentTable(keepDocComments, fac);
this.keepLineMap = keepLineMap;
@ -211,6 +213,10 @@ public class JavacParser implements Parser {
*/
boolean allowStaticInterfaceMethods;
/** Switch: should we allow private (instance) methods in interfaces?
*/
boolean allowPrivateInterfaceMethods;
/** Switch: should we allow intersection types in cast?
*/
boolean allowIntersectionTypesInCast;
@ -3487,9 +3493,14 @@ public class JavacParser implements Parser {
List<JCTypeParameter> typarams,
boolean isInterface, boolean isVoid,
Comment dc) {
if (isInterface && (mods.flags & Flags.STATIC) != 0) {
if (isInterface) {
if ((mods.flags & Flags.STATIC) != 0) {
checkStaticInterfaceMethods();
}
if ((mods.flags & Flags.PRIVATE) != 0) {
checkPrivateInterfaceMethods();
}
}
JCVariableDecl prevReceiverParam = this.receiverParam;
try {
this.receiverParam = null;
@ -4002,6 +4013,12 @@ public class JavacParser implements Parser {
allowTypeAnnotations = true;
}
}
void checkPrivateInterfaceMethods() {
if (!allowPrivateInterfaceMethods) {
log.error(token.pos, CompilerProperties.Errors.PrivateIntfMethodsNotSupportedInSource(source.name));
allowPrivateInterfaceMethods = true;
}
}
protected void checkAnnotationsAfterTypeParams(int pos) {
if (!allowAnnotationsAfterTypeParams) {
log.error(pos, "annotations.after.type.params.not.supported.in.source", source.name);

View File

@ -2421,6 +2421,11 @@ compiler.err.static.intf.method.invoke.not.supported.in.source=\
static interface method invocations are not supported in -source {0}\n\
(use -source 8 or higher to enable static interface method invocations)
# 0: string
compiler.err.private.intf.methods.not.supported.in.source=\
private interface methods are not supported in -source {0}\n\
(use -source 9 or higher to enable private interface methods)
########################################
# Diagnostics for verbose resolution
# used by Resolve (debug only)

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8071453
* @summary smoke test for private interface methods featuring accessor methods
*/
public class Private01 {
interface P {
private void foo() { System.out.println("foo!" + this); }
default void m() {
new Object() { void test() { foo(); } }.test();
}
}
public static void main(String[] args) {
P p = new P() {};
p.m(); p.foo();
}
}

View File

@ -0,0 +1,24 @@
/*
* @test /nodynamiccopyright/
* @bug 8071453
* @author sadayapalam
* @summary Various tests for private methods in interfaces.
* @compile/fail/ref=Private02.out -XDrawDiagnostics Private02.java
*/
public class Private02 {
interface I {
private void foo(String s); // Error: private method must declare body.
private abstract void foo(int i, int j); // Error: private & abstract: bad combo
void foo(int x); // OK.
private I foo() { return null; } // OK.
private void foo(int x) {} // Name clash.
}
interface J extends I {
private J foo() { return null; } // OK.
}
interface K extends J {
void foo(); // OK
}
}

View File

@ -0,0 +1,4 @@
Private02.java:13:31: compiler.err.illegal.combination.of.modifiers: abstract, private
Private02.java:16:22: compiler.err.already.defined: kindname.method, foo(int), kindname.interface, Private02.I
Private02.java:12:22: compiler.err.missing.meth.body.or.decl.abstract
3 errors

View File

@ -0,0 +1,27 @@
/*
* @test /nodynamiccopyright/
* @bug 8071453
* @author sadayapalam
* @summary Various tests for private methods in interfaces.
* @compile/fail/ref=Private03.out -XDrawDiagnostics Private03.java
*/
public class Private03 {
interface I {
private void foo(int x) {}
private void goo(int x) {}
}
interface J extends I {
// Verify that we are able to declare a public abstract method with the same signature as a private method in super type.
void foo(int x);
// Verify that we are able to declare a public default method with the same signature as a private method in super type.
default void goo(int x) {}
}
interface K extends J {
private void foo(int x) {} // Error, cannot reduce visibility
private void goo(int x) {} // Ditto.
}
}

View File

@ -0,0 +1,3 @@
Private03.java:25:22: compiler.err.override.weaker.access: (compiler.misc.clashes.with: goo(int), Private03.K, goo(int), Private03.J), public
Private03.java:24:22: compiler.err.override.weaker.access: (compiler.misc.clashes.with: foo(int), Private03.K, foo(int), Private03.J), public
2 errors

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8071453
* @author sadayapalam
* @summary Verify that adding private methods does not disqualify an interface from being functional
*/
public class Private04 {
@FunctionalInterface
interface SubRunnable extends Runnable {
private void run(int x) {
SubRunnable s = () -> {};
}
}
public static void main(String [] args) {
SubRunnable s = () -> {};
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8071453
* @author sadayapalam
* @summary Execution test for private interface methods (instance and static)
*/
public interface Private05 {
private static String staticPrivate() {
return "static private";
}
private String instancePrivate() {
return "instance private";
}
public static void main(String [] args) {
String result = staticPrivate();
if (!result.equals("static private"))
throw new AssertionError("Incorrect result for static private interface method");
Private05 pvt = new Private05() {};
result = pvt.instancePrivate();
if (!result.equals("instance private"))
throw new AssertionError("Incorrect result for instance private interface method");
}
}

View File

@ -0,0 +1,30 @@
/* @test /nodynamiccopyright/
* @bug 8071453
* @author sadayapalam
* @summary Test that a lone private interface method cannot supply the SAM.
* @compile/fail/ref=Private06.out -XDrawDiagnostics Private06.java
*/
public class Private06 {
@FunctionalInterface
interface NAFI {
private void foo() {
}
}
@FunctionalInterface
interface FI {
void foo(NAFI nafi);
}
public static void main(String [] args) {
Private06.NAFI nafi = () -> {};
Private06.FI fi = Private06.NAFI::foo; // OK.
}
}
class Private06_01 {
public static void main(String [] args) {
Private06.FI fi = Private06.NAFI::foo; // NOT OK.
}
}

View File

@ -0,0 +1,4 @@
Private06.java:9:5: compiler.err.bad.functional.intf.anno.1: (compiler.misc.not.a.functional.intf.1: Private06.NAFI, (compiler.misc.no.abstracts: kindname.interface, Private06.NAFI))
Private06.java:21:31: compiler.err.prob.found.req: (compiler.misc.not.a.functional.intf.1: Private06.NAFI, (compiler.misc.no.abstracts: kindname.interface, Private06.NAFI))
Private06.java:28:27: compiler.err.prob.found.req: (compiler.misc.invalid.mref: kindname.method, (compiler.misc.report.access: foo(), private, Private06.NAFI))
3 errors

View File

@ -0,0 +1,10 @@
/* @test /nodynamiccopyright/
* @bug 8071453
* @author sadayapalam
* @summary Test that annotations types cannot declare private methods
* @compile/fail/ref=Private07.out -XDrawDiagnostics Private07.java
*/
@interface Private07 {
private String name();
}

View File

@ -0,0 +1,2 @@
Private07.java:9:20: compiler.err.mod.not.allowed.here: private
1 error

View File

@ -0,0 +1,37 @@
/* @test /nodynamiccopyright/
* @bug 8071453
* @author sadayapalam
* @summary Test various JLS changes made for supporting private interface methods.
* @compile/fail/ref=Private08.out -XDrawDiagnostics Private08.java
*/
class Private08 {
interface I {
private void poo() {}
private int foo() { return 0; }
int goo();
default int doo() { return foo(); }
private public int bad(); // 9.4 illegal combination of modifiers
private abstract int verybad(); // 9.4 illegal combination of modifiers
private default int alsobad() { return foo(); } // 9.4 illegal combination of modifiers
protected void blah();
private void missingBody(); // private methods are not abstract.
}
}
class Private08_01 {
int y = ((Private08.I) null).foo(); // 9.4 test that private methods are not implicitly public.
interface J extends Private08.I {
default void foo() { // foo not inherited from super, change of return type is OK.
super.foo(); // super in static context - Error.
}
private int doo() { return 0; } // private cannot override public.
};
Private08.I i = new Private08.I () {
public void foo() { // foo not inherited from super, change of return type is OK.
super.foo(); // super's foo not inherited, NOT OK.
}
private int doo() { return 0; } // private cannot override public.
}; // should not complain about poo() not being implemented.
}

View File

@ -0,0 +1,13 @@
Private08.java:13:28: compiler.err.illegal.combination.of.modifiers: public, private
Private08.java:14:30: compiler.err.illegal.combination.of.modifiers: abstract, private
Private08.java:15:29: compiler.err.illegal.combination.of.modifiers: private, default
Private08.java:16:24: compiler.err.mod.not.allowed.here: protected
Private08.java:17:22: compiler.err.missing.meth.body.or.decl.abstract
Private08.java:22:33: compiler.err.report.access: foo(), private, Private08.I
Private08.java:27:21: compiler.err.override.weaker.access: (compiler.misc.clashes.with: doo(), Private08_01.J, doo(), Private08.I), public
Private08.java:25:13: compiler.err.non-static.cant.be.ref: kindname.variable, super
Private08.java:25:18: compiler.err.cant.resolve.args: kindname.method, foo, ,
Private08.java:30:40: compiler.err.does.not.override.abstract: compiler.misc.anonymous.class: Private08_01$1, blah(), Private08.I
Private08.java:34:21: compiler.err.override.weaker.access: (compiler.misc.cant.implement: doo(), compiler.misc.anonymous.class: Private08_01$1, doo(), Private08.I), public
Private08.java:32:18: compiler.err.cant.resolve.args: kindname.method, foo, ,
12 errors

View File

@ -0,0 +1,11 @@
/* @test /nodynamiccopyright/
* @bug 8071453
* @author sadayapalam
* @summary Test various JLS changes made for supporting private interface methods.
* @compile/fail/ref=Private09.out -XDrawDiagnostics Private09.java
*/
class Private09 {
interface I {
private private void poo() {}
}
}

View File

@ -0,0 +1,2 @@
Private09.java:9:17: compiler.err.repeated.modifier
1 error

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -23,7 +23,7 @@
/*
* @test
* @bug 7192245 8005851 8005166
* @bug 7192245 8005851 8005166 8071453
* @summary Automatic test for checking set of allowed modifiers on interface methods
*/
@ -45,7 +45,8 @@ public class TestDefaultMethodsSyntax {
enum VersionKind {
PRE_LAMBDA("7"),
LAMBDA("8");
LAMBDA("8"),
POST_LAMBDA("9");
String versionString;
@ -87,7 +88,8 @@ public class TestDefaultMethodsSyntax {
static boolean compatible(MethodKind mk, ModifierKind mod1, ModifierKind mod2, EnclosingKind ek) {
if (intersect(ABSTRACT, mod1, mod2) || intersect(NATIVE, mod1, mod2)) {
return mk == MethodKind.NO_BODY;
} else if (intersect(DEFAULT, mod1, mod2) || intersect(STATIC, mod1, mod2)) {
} else if (intersect(DEFAULT, mod1, mod2) || intersect(STATIC, mod1, mod2)
|| intersect(PRIVATE, mod1, mod2)) {
return mk == MethodKind.BODY;
} else {
return ek == EnclosingKind.INTERFACE ?
@ -97,7 +99,6 @@ public class TestDefaultMethodsSyntax {
boolean compatible(EnclosingKind ek) {
switch (this) {
case PRIVATE:
case PROTECTED:
return ek != EnclosingKind.INTERFACE;
default:
@ -149,16 +150,16 @@ public class TestDefaultMethodsSyntax {
static Result[][] allowedModifierPairs = {
/* NONE PUBLIC PROTECTED PRIVATE ABSTRACT STATIC NATIVE SYNCHRONIZED FINAL STRICTFP DEFAULT */
/* NONE */ { T , T , C , C , T , T , C , C , C , C , I },
/* NONE */ { T , T , C , T , T , T , C , C , C , C , I },
/* PUBLIC */ { T , F , F , F , T , T , C , C , C , C , I },
/* PROTECTED */ { C , F , F , F , C , C , C , C , C , C , F },
/* PRIVATE */ { C , F , F , F , F , C , C , C , C , C , F },
/* PRIVATE */ { T , F , F , F , F , T , C , C , C , T , F },
/* ABSTRACT */ { T , T , C , F , F , F , F , F , F , F , F },
/* STATIC */ { T , T , C , C , F , F , C , C , C , T , F },
/* STATIC */ { T , T , C , T , F , F , C , C , C , T , F },
/* NATIVE */ { C , C , C , C , F , C , F , C , C , F , F },
/* SYNCHRONIZED */ { C , C , C , C , F , C , C , F , C , C , F },
/* FINAL */ { C , C , C , C , F , C , C , C , F , C , F },
/* STRICTFP */ { C , C , C , C , F , T , F , C , C , F , I },
/* STRICTFP */ { C , C , C , T , F , T , F , C , C , F , I },
/* DEFAULT */ { I , I , F , F , F , F , F , F , F , I , F }};
}
@ -268,6 +269,9 @@ public class TestDefaultMethodsSyntax {
errorExpected |= ModifierKind.intersect(ModifierKind.STATIC, modk1, modk2) &&
ek == EnclosingKind.INTERFACE && vk == VersionKind.PRE_LAMBDA;
errorExpected |= ModifierKind.intersect(ModifierKind.PRIVATE, modk1, modk2) &&
ek == EnclosingKind.INTERFACE && (vk == VersionKind.LAMBDA || vk == VersionKind.PRE_LAMBDA);
checkCount++;
if (diagChecker.errorFound != errorExpected) {
throw new AssertionError("Problem when compiling source:\n" + source.getCharContent(true) +

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// key: compiler.err.private.intf.methods.not.supported.in.source
// key: compiler.warn.source.no.bootclasspath
// options: -source 8
interface PrivateInterfaceMethodsNotSupported {
private void foo() {}
}