Enhancement: Add support for static interface methods
authormcimadamore
Mon Dec 03 15:32:06 2012 +0000 (5 months ago)
changeset 165667030038d40b
parent 1655c72ca936299c
child 16577edd4d92d95c
Enhancement: Add support for static interface methods

This patch adds support for static interface methods.
Hiding rules are simpler than those for static class methods, as a static interface method cannot be inherithed.
src/share/classes/com/sun/tools/javac/code/Flags.java
src/share/classes/com/sun/tools/javac/code/Source.java
src/share/classes/com/sun/tools/javac/code/Symbol.java
src/share/classes/com/sun/tools/javac/comp/Attr.java
src/share/classes/com/sun/tools/javac/comp/Check.java
src/share/classes/com/sun/tools/javac/parser/JavacParser.java
src/share/classes/com/sun/tools/javac/resources/compiler.properties
test/tools/javac/defaultMethods/hiding/InterfaceMethodHidingTest.java
test/tools/javac/defaultMethods/syntax/TestDefaultMethodsSyntax.java
test/tools/javac/diags/examples.not-yet.txt
--- a/src/share/classes/com/sun/tools/javac/code/Flags.java Wed Nov 28 14:14:15 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/code/Flags.java Mon Dec 03 15:32:06 2012 +0000
@@ -71,6 +71,7 @@ public class Flags {
if ((mask&BRIDGE) != 0) flags.add(Flag.BRIDGE);
if ((mask&SYNTHETIC) != 0) flags.add(Flag.SYNTHETIC);
if ((mask&DEPRECATED) != 0) flags.add(Flag.DEPRECATED);
+ if ((mask&DEFAULT) != 0) flags.add(Flag.DEFAULT);
if ((mask&HASINIT) != 0) flags.add(Flag.HASINIT);
if ((mask&ENUM) != 0) flags.add(Flag.ENUM);
if ((mask&IPROXY) != 0) flags.add(Flag.IPROXY);
@@ -279,7 +280,7 @@ public class Flags {
SYNCHRONIZED | FINAL | STRICTFP;
public static final long
ExtendedStandardFlags = (long)StandardFlags | DEFAULT,
- InterfaceDefaultMethodMask = ABSTRACT | PUBLIC | STRICTFP | SYNCHRONIZED | DEFAULT,
+ InterfaceMethodMask = ABSTRACT | STATIC | PUBLIC | STRICTFP | SYNCHRONIZED | DEFAULT,
LocalVarFlags = FINAL | PARAMETER;
--- a/src/share/classes/com/sun/tools/javac/code/Source.java Wed Nov 28 14:14:15 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/code/Source.java Mon Dec 03 15:32:06 2012 +0000
@@ -204,6 +204,9 @@ public enum Source {
return compareTo(JDK1_8) >= 0;
}
public boolean allowDefaultMethods() {
+ return compareTo(JDK1_8) >= 0;
+ }
+ public boolean allowStaticInterfaceMethods() {
return compareTo(JDK1_8) >= 0;
}
public boolean allowStrictMethodClashCheck() {
--- a/src/share/classes/com/sun/tools/javac/code/Symbol.java Wed Nov 28 14:14:15 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/code/Symbol.java Mon Dec 03 15:32:06 2012 +0000
@@ -386,7 +386,7 @@ public abstract class Symbol implements
}
return (clazz.flags() & INTERFACE) == 0;
}
- }
+ }
/** The (variable or method) symbol seen as a member of given
* class type`site' (this might change the symbol's type).
@@ -1213,7 +1213,8 @@ public abstract class Symbol implements
case Flags.PRIVATE:
return false;
case Flags.PUBLIC:
- return true;
+ return !this.owner.isInterface() ||
+ (flags_field & STATIC) == 0;
case Flags.PROTECTED:
return (origin.flags() & INTERFACE) == 0;
case 0:
@@ -1224,6 +1225,17 @@ public abstract class Symbol implements
(origin.flags() & INTERFACE) == 0;
default:
return false;
+ }
+ }
+
+ @Override
+ public boolean isInheritedIn(Symbol clazz, Types types) {
+ switch ((int)(flags_field & Flags.AccessFlags)) {
+ case PUBLIC:
+ return !this.owner.isInterface() ||
+ (flags_field & STATIC) == 0;
+ default:
+ return super.isInheritedIn(clazz, types);
}
}
--- a/src/share/classes/com/sun/tools/javac/comp/Attr.java Wed Nov 28 14:14:15 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java Mon Dec 03 15:32:06 2012 +0000
@@ -954,8 +954,7 @@ public class Attr extends JCTree.Visitor
// Empty bodies are only allowed for
// abstract, native, or interface methods, or for methods
// in a retrofit signature class.
- if (isDefaultMethod || ((owner.flags() & INTERFACE) == 0 &&
- (tree.mods.flags & (ABSTRACT | NATIVE)) == 0) &&
+ if (isDefaultMethod || (tree.sym.flags() & (ABSTRACT | NATIVE)) == 0 &&
!relax)
log.error(tree.pos(), "missing.meth.body.or.decl.abstract");
if (tree.defaultValue != null) {
--- a/src/share/classes/com/sun/tools/javac/comp/Check.java Wed Nov 28 14:14:15 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/comp/Check.java Mon Dec 03 15:32:06 2012 +0000
@@ -1072,9 +1072,12 @@ public class Check {
} else
mask = ConstructorFlags;
} else if ((sym.owner.flags_field & INTERFACE) != 0) {
- if ((flags & DEFAULT) != 0) {
- mask = InterfaceDefaultMethodMask;
- implicit = PUBLIC | ABSTRACT;
+ if ((flags & (DEFAULT | STATIC)) != 0) {
+ mask = InterfaceMethodMask;
+ implicit = PUBLIC;
+ if ((flags & DEFAULT) != 0) {
+ implicit |= ABSTRACT;
+ }
} else {
mask = implicit = InterfaceMethodFlags;
}
@@ -1142,6 +1145,10 @@ public class Check {
checkDisjoint(pos, flags,
ABSTRACT,
PRIVATE | STATIC | DEFAULT))
+ &&
+ checkDisjoint(pos, flags,
+ STATIC,
+ DEFAULT)
&&
checkDisjoint(pos, flags,
ABSTRACT | INTERFACE,
--- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Wed Nov 28 14:14:15 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Mon Dec 03 15:32:06 2012 +0000
@@ -124,6 +124,7 @@ public class JavacParser implements Pars
this.allowLambda = source.allowLambda();
this.allowMethodReferences = source.allowMethodReferences();
this.allowDefaultMethods = source.allowDefaultMethods();
+ this.allowStaticInterfaceMethods = source.allowStaticInterfaceMethods();
this.keepDocComments = keepDocComments;
docComments = newDocCommentTable(keepDocComments);
this.keepLineMap = keepLineMap;
@@ -196,6 +197,10 @@ public class JavacParser implements Pars
/** Switch: should we allow default methods in interfaces?
*/
boolean allowDefaultMethods;
+
+ /** Switch: should we allow static methods in interfaces?
+ */
+ boolean allowStaticInterfaceMethods;
/** Switch: should we keep docComments?
*/
@@ -3078,6 +3083,9 @@ public class JavacParser implements Pars
List<JCTypeParameter> typarams,
boolean isInterface, boolean isVoid,
Comment dc) {
+ if (isInterface && mods.getFlags().contains(Modifier.STATIC)) {
+ checkStaticInterfaceMethods();
+ }
List<JCVariableDecl> params = formalParameters();
if (!isVoid) type = bracketsOpt(type);
List<JCExpression> thrown = List.nil();
@@ -3467,6 +3475,12 @@ public class JavacParser implements Pars
allowDefaultMethods = true;
}
}
+ void checkStaticInterfaceMethods() {
+ if (!allowStaticInterfaceMethods) {
+ log.error(token.pos, "static.intf.methods.not.supported.in.source", source.name);
+ allowStaticInterfaceMethods = true;
+ }
+ }
/*
* a functional source tree and end position mappings
--- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties Wed Nov 28 14:14:15 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties Mon Dec 03 15:32:06 2012 +0000
@@ -2206,6 +2206,11 @@ compiler.err.default.methods.not.support
default methods are not supported in -source {0}\n\
(use -source 8 or higher to enable default methods)
+# 0: string
+compiler.err.static.intf.methods.not.supported.in.source=\
+ static interface methods are not supported in -source {0}\n\
+ (use -source 8 or higher to enable static interface methods)
+
########################################
# Diagnostics for verbose resolution
# used by Resolve (debug only)
--- a/test/tools/javac/defaultMethods/syntax/TestDefaultMethodsSyntax.java Wed Nov 28 14:14:15 2012 +0000
+++ b/test/tools/javac/defaultMethods/syntax/TestDefaultMethodsSyntax.java Mon Dec 03 15:32:06 2012 +0000
@@ -77,32 +77,6 @@ public class TestDefaultMethodsSyntax {
this.modStr = modStr;
}
- boolean isAllowed(EnclosingKind ek, ModifierKind otherMod) {
- if (this == otherMod) return false;
- switch (this) {
- case NONE:
- return true;
- case ABSTRACT:
- return otherMod != PRIVATE;
- case NATIVE:
- return otherMod != ABSTRACT &&
- otherMod != STRICTFP;
- case FINAL:
- case STATIC:
- case SYNCHRONIZED:
- case STRICTFP:
- return otherMod != ABSTRACT;
- case PUBLIC:
- return true;
- case PROTECTED:
- return ek == EnclosingKind.ABSTRACT_CLASS;
- case DEFAULT:
- return otherMod != ABSTRACT;
- default:
- return true;
- }
- }
-
static boolean intersect(ModifierKind mk, ModifierKind... mks) {
for (ModifierKind mk2 : mks) {
if (mk == mk2) return true;
@@ -113,7 +87,7 @@ 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)) {
+ } else if (intersect(DEFAULT, mod1, mod2) || intersect(STATIC, mod1, mod2)) {
return mk == MethodKind.BODY;
} else {
return ek == EnclosingKind.INTERFACE ?
@@ -123,7 +97,6 @@ public class TestDefaultMethodsSyntax {
boolean compatible(EnclosingKind ek) {
switch (this) {
- case STATIC:
case PRIVATE:
case PROTECTED:
return ek != EnclosingKind.INTERFACE;
@@ -176,16 +149,16 @@ public class TestDefaultMethodsSyntax {
static Result[][] allowedModifierPairs = {
/* NONE PUBLIC PROTECTED PRIVATE ABSTRACT STATIC NATIVE SYNCHRONIZED FINAL STRICTFP DEFAULT */
- /* NONE */ { T , T , C , C , T , C , C , C , C , C , I },
- /* PUBLIC */ { T , F , F , F , T , C , C , C , C , C , I },
+ /* NONE */ { T , T , C , C , 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 },
/* ABSTRACT */ { T , T , C , F , F , F , F , F , F , F , F },
- /* STATIC */ { C , C , C , C , F , F , C , C , C , C , F },
+ /* STATIC */ { T , T , C , C , F , F , C , T , 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 , I },
+ /* SYNCHRONIZED */ { C , C , C , C , F , T , C , F , C , C , I },
/* FINAL */ { C , C , C , C , F , C , C , C , F , C , F },
- /* STRICTFP */ { C , C , C , C , F , C , F , C , C , F , I },
+ /* STRICTFP */ { C , C , C , C , F , T , F , C , C , F , I },
/* DEFAULT */ { I , I , F , F , F , F , F , I , F , I , F }};
}
@@ -291,6 +264,9 @@ public class TestDefaultMethodsSyntax {
errorExpected |= ModifierKind.intersect(ModifierKind.DEFAULT, modk1, modk2) &&
vk == VersionKind.PRE_LAMBDA;
+ errorExpected |= ModifierKind.intersect(ModifierKind.STATIC, modk1, modk2) &&
+ ek == EnclosingKind.INTERFACE && vk == VersionKind.PRE_LAMBDA;
+
checkCount++;
if (diagChecker.errorFound != errorExpected) {
throw new AssertionError("Problem when compiling source:\n" + source.getCharContent(true) +
--- a/test/tools/javac/diags/examples.not-yet.txt Wed Nov 28 14:14:15 2012 +0000
+++ b/test/tools/javac/diags/examples.not-yet.txt Mon Dec 03 15:32:06 2012 +0000
@@ -109,3 +109,4 @@ compiler.misc.static.mref.with.targs
compiler.misc.static.mref.with.targs #LAMBDA
compiler.misc.static.bound.mref #LAMBDA
compiler.misc.secondary.bound.must.be.marker.intf #LAMBDA
+compiler.err.static.intf.methods.not.supported.in.source #LAMBDA
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/defaultMethods/hiding/InterfaceMethodHidingTest.java Mon Dec 03 15:32:06 2012 +0000
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2012, 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
+ * @summary Smoke test for static interface method hiding
+ */
+
+import com.sun.source.util.JavacTask;
+import java.net.URI;
+import java.util.Arrays;
+import javax.tools.Diagnostic;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+
+
+public class InterfaceMethodHidingTest {
+
+ static int checkCount = 0;
+
+ enum SignatureKind {
+ VOID_INTEGER("void m(Integer s)", "return;"),
+ STRING_INTEGER("String m(Integer s)", "return null;"),
+ VOID_STRING("void m(String s)", "return;"),
+ STRING_STRING("String m(String s)", "return null;");
+
+ String sigStr;
+ String retStr;
+
+ SignatureKind(String sigStr, String retStr) {
+ this.sigStr = sigStr;
+ this.retStr = retStr;
+ }
+
+ boolean overrideEquivalentWith(SignatureKind s2) {
+ switch (this) {
+ case VOID_INTEGER:
+ case STRING_INTEGER:
+ return s2 == VOID_INTEGER || s2 == STRING_INTEGER;
+ case VOID_STRING:
+ case STRING_STRING:
+ return s2 == VOID_STRING || s2 == STRING_STRING;
+ default:
+ throw new AssertionError("bad signature kind");
+ }
+ }
+ }
+
+ enum MethodKind {
+ VIRTUAL("", "#M #S;"),
+ STATIC("static", "#M #S { #BE; #R }"),
+ DEFAULT("default", "#M #S { #BE; #R }");
+
+ String modStr;
+ String methTemplate;
+
+ MethodKind(String modStr, String methTemplate) {
+ this.modStr = modStr;
+ this.methTemplate = methTemplate;
+ }
+
+ boolean inherithed() {
+ return this != STATIC;
+ }
+
+ static boolean overrides(MethodKind mk1, SignatureKind sk1, MethodKind mk2, SignatureKind sk2) {
+ return sk1 == sk2 &&
+ mk2.inherithed() &&
+ mk1 != STATIC;
+ }
+
+ String getBody(BodyExpr be, SignatureKind sk) {
+ return methTemplate.replaceAll("#BE", be.bodyExprStr)
+ .replaceAll("#R", sk.retStr)
+ .replaceAll("#M", modStr)
+ .replaceAll("#S", sk.sigStr);
+ }
+ }
+
+ enum BodyExpr {
+ NONE(""),
+ THIS("Object o = this");
+
+ String bodyExprStr;
+
+ BodyExpr(String bodyExprStr) {
+ this.bodyExprStr = bodyExprStr;
+ }
+
+ boolean allowed(MethodKind mk) {
+ return this == NONE ||
+ mk != MethodKind.STATIC;
+ }
+ }
+
+ public static void main(String... args) throws Exception {
+
+ //create default shared JavaCompiler - reused across multiple compilations
+ JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
+ StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
+
+ for (MethodKind mk1 : MethodKind.values()) {
+ for (SignatureKind sk1 : SignatureKind.values()) {
+ for (BodyExpr be1 : BodyExpr.values()) {
+ for (MethodKind mk2 : MethodKind.values()) {
+ for (SignatureKind sk2 : SignatureKind.values()) {
+ for (BodyExpr be2 : BodyExpr.values()) {
+ for (MethodKind mk3 : MethodKind.values()) {
+ for (SignatureKind sk3 : SignatureKind.values()) {
+ for (BodyExpr be3 : BodyExpr.values()) {
+ new InterfaceMethodHidingTest(mk1, mk2, mk3, sk1, sk2, sk3, be1, be2, be3).run(comp, fm);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ System.out.println("Total check executed: " + checkCount);
+ }
+
+ MethodKind mk1, mk2, mk3;
+ SignatureKind sk1, sk2, sk3;
+ BodyExpr be1, be2, be3;
+ JavaSource source;
+ DiagnosticChecker diagChecker;
+
+ InterfaceMethodHidingTest(MethodKind mk1, MethodKind mk2, MethodKind mk3,
+ SignatureKind sk1, SignatureKind sk2, SignatureKind sk3, BodyExpr be1, BodyExpr be2, BodyExpr be3) {
+ this.mk1 = mk1;
+ this.mk2 = mk2;
+ this.mk3 = mk3;
+ this.sk1 = sk1;
+ this.sk2 = sk2;
+ this.sk3 = sk3;
+ this.be1 = be1;
+ this.be2 = be2;
+ this.be3 = be3;
+ this.source = new JavaSource();
+ this.diagChecker = new DiagnosticChecker();
+ }
+
+ class JavaSource extends SimpleJavaFileObject {
+
+ String template = "interface Sup {\n" +
+ " default void sup() { }\n" +
+ "}\n" +
+ "interface A extends Sup {\n" +
+ " #M1\n" +
+ "}\n" +
+ "interface B extends A, Sup {\n" +
+ " #M2\n" +
+ "}\n" +
+ "interface C extends B, Sup {\n" +
+ " #M3\n" +
+ "}\n";
+
+ String source;
+
+ public JavaSource() {
+ super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+ source = template.replaceAll("#M1", mk1.getBody(be1, sk1))
+ .replaceAll("#M2", mk2.getBody(be2, sk2))
+ .replaceAll("#M3", mk3.getBody(be3, sk3));
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return source;
+ }
+ }
+
+ void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
+ JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
+ null, null, Arrays.asList(source));
+ try {
+ ct.analyze();
+ } catch (Throwable ex) {
+ throw new AssertionError("Error thrown when analyzing the following source:\n" + source.getCharContent(true));
+ }
+ check();
+ }
+
+ void check() {
+ boolean errorExpected =
+ !be1.allowed(mk1) || !be2.allowed(mk2) || !be3.allowed(mk3);
+
+ if (mk1.inherithed()) {
+ errorExpected |=
+ sk2.overrideEquivalentWith(sk1) && !MethodKind.overrides(mk2, sk2, mk1, sk1) ||
+ sk3.overrideEquivalentWith(sk1) && !MethodKind.overrides(mk3, sk3, mk1, sk1);
+ }
+
+ if (mk2.inherithed()) {
+ errorExpected |=
+ sk3.overrideEquivalentWith(sk2) && !MethodKind.overrides(mk3, sk3, mk2, sk2);
+ }
+
+ checkCount++;
+ if (diagChecker.errorFound != errorExpected) {
+ throw new AssertionError("Problem when compiling source:\n" + source.getCharContent(true) +
+ "\nfound error: " + diagChecker.errorFound);
+ }
+ }
+
+ static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
+
+ boolean errorFound;
+
+ public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+ if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
+ errorFound = true;
+ }
+ }
+ }
+}