changeset 1656:67030038d40b

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.
author mcimadamore
date Mon, 03 Dec 2012 15:32:06 +0000
parents c72ca936299c
children 7edd4d92d95c
files 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
diffstat 10 files changed, 300 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- 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 @@
         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 @@
                                 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
@@ -206,6 +206,9 @@
     public boolean allowDefaultMethods() {
         return compareTo(JDK1_8) >= 0;
     }
+    public boolean allowStaticInterfaceMethods() {
+        return compareTo(JDK1_8) >= 0;
+    }
     public boolean allowStrictMethodClashCheck() {
         return compareTo(JDK1_8) >= 0;
     }
--- 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 @@
             }
             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 @@
             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:
@@ -1226,6 +1227,17 @@
                 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);
+            }
+        }
 
         /** The implementation of this (abstract) symbol in class origin;
          *  null if none exists. Synthetic methods are not considered
--- 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 @@
                 // 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 @@
                 } 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;
                 }
@@ -1144,6 +1147,10 @@
                                 PRIVATE | STATIC | DEFAULT))
                  &&
                  checkDisjoint(pos, flags,
+                                STATIC,
+                                DEFAULT)
+                 &&
+                 checkDisjoint(pos, flags,
                                ABSTRACT | INTERFACE,
                                FINAL | NATIVE | SYNCHRONIZED)
                  &&
--- 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 @@
         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 @@
     /** 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 @@
                               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 @@
             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 @@
     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)
--- /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;
+            }
+        }
+    }
+}
--- 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 @@
             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 @@
         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 @@
 
         boolean compatible(EnclosingKind ek) {
             switch (this) {
-                case STATIC:
                 case PRIVATE:
                 case PROTECTED:
                     return ek != EnclosingKind.INTERFACE;
@@ -176,16 +149,16 @@
 
         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 @@
         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                                             #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