8078600: Infinite loop when compiling annotations with -XDcompletionDeps
authoralundblad
Tue, 28 Apr 2015 22:25:36 +0200
changeset 30066 d74c06a92bd8
parent 30065 a3873788f1b4
child 30067 08fb37c9b670
8078600: Infinite loop when compiling annotations with -XDcompletionDeps Summary: Added Completer::isTerminal and added NULL_COMPLETER. Reviewed-by: jlahoda, mcimadamore
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ClassFinder.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Dependencies.java
langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/ClassDocImpl.java
langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/JavadocTool.java
langtools/test/tools/javac/completionDeps/DepsAndAnno.java
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ClassFinder.java	Tue Apr 28 11:08:25 2015 +0300
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ClassFinder.java	Tue Apr 28 22:25:36 2015 +0200
@@ -128,7 +128,7 @@
      *  the completer to be used for ".java" files. If this remains unassigned
      *  ".java" files will not be loaded.
      */
-    public Completer sourceCompleter = null;
+    public Completer sourceCompleter = Completer.NULL_COMPLETER;
 
     /** The path name of the class file currently being read.
      */
@@ -341,7 +341,7 @@
                     reader.readClassFile(c);
                     c.flags_field |= getSupplementaryFlags(c);
                 } else {
-                    if (sourceCompleter != null) {
+                    if (!sourceCompleter.isTerminal()) {
                         sourceCompleter.complete(c);
                     } else {
                         throw new IllegalStateException("Source completer required to read "
@@ -392,7 +392,7 @@
     public ClassSymbol loadClass(Name flatname) throws CompletionFailure {
         boolean absent = syms.classes.get(flatname) == null;
         ClassSymbol c = syms.enterClass(flatname);
-        if (c.members_field == null && c.completer != null) {
+        if (c.members_field == null) {
             try {
                 c.complete();
             } catch (CompletionFailure ex) {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Tue Apr 28 11:08:25 2015 +0300
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Tue Apr 28 22:25:36 2015 +0200
@@ -96,6 +96,7 @@
     public Symbol owner;
 
     /** The completer of this symbol.
+     * This should never equal null (NULL_COMPLETER should be used instead).
      */
     public Completer completer;
 
@@ -193,6 +194,10 @@
         return (metadata != null && !metadata.isTypesEmpty());
     }
 
+    public boolean isCompleted() {
+        return completer.isTerminal();
+    }
+
     public void prependAttributes(List<Attribute.Compound> l) {
         if (l.nonEmpty()) {
             initedMetadata().prepend(l);
@@ -243,7 +248,7 @@
         this.flags_field = flags;
         this.type = type;
         this.owner = owner;
-        this.completer = null;
+        this.completer = Completer.NULL_COMPLETER;
         this.erasure_field = null;
         this.name = name;
     }
@@ -568,9 +573,9 @@
     /** Complete the elaboration of this symbol's definition.
      */
     public void complete() throws CompletionFailure {
-        if (completer != null) {
+        if (completer != Completer.NULL_COMPLETER) {
             Completer c = completer;
-            completer = null;
+            completer = Completer.NULL_COMPLETER;
             c.complete(this);
         }
     }
@@ -872,19 +877,19 @@
         }
 
         public WriteableScope members() {
-            if (completer != null) complete();
+            complete();
             return members_field;
         }
 
         public long flags() {
-            if (completer != null) complete();
+            complete();
             return flags_field;
         }
 
         @Override
         public List<Attribute.Compound> getRawAttributes() {
-            if (completer != null) complete();
-            if (package_info != null && package_info.completer != null) {
+            complete();
+            if (package_info != null) {
                 package_info.complete();
                 mergeAttributes();
             }
@@ -1000,24 +1005,24 @@
         }
 
         public long flags() {
-            if (completer != null) complete();
+            complete();
             return flags_field;
         }
 
         public WriteableScope members() {
-            if (completer != null) complete();
+            complete();
             return members_field;
         }
 
         @Override
         public List<Attribute.Compound> getRawAttributes() {
-            if (completer != null) complete();
+            complete();
             return super.getRawAttributes();
         }
 
         @Override
         public List<Attribute.TypeCompound> getRawTypeAttributes() {
-            if (completer != null) complete();
+            complete();
             return super.getRawTypeAttributes();
         }
 
@@ -1782,7 +1787,29 @@
     /** Symbol completer interface.
      */
     public static interface Completer {
+
+        /** Dummy completer to be used when the symbol has been completed or
+         * does not need completion.
+         */
+        public final static Completer NULL_COMPLETER = new Completer() {
+            public void complete(Symbol sym) { }
+            public boolean isTerminal() { return true; }
+        };
+
         void complete(Symbol sym) throws CompletionFailure;
+
+        /** Returns true if this completer is <em>terminal</em>. A terminal
+         * completer is used as a place holder when the symbol is completed.
+         * Calling complete on a terminal completer will not affect the symbol.
+         *
+         * The dummy NULL_COMPLETER and the GraphDependencies completer are
+         * examples of terminal completers.
+         *
+         * @return true iff this completer is terminal
+         */
+        default boolean isTerminal() {
+            return false;
+        }
     }
 
     public static class CompletionFailure extends RuntimeException {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Tue Apr 28 11:08:25 2015 +0300
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Tue Apr 28 22:25:36 2015 +0200
@@ -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
@@ -259,49 +259,54 @@
 
     public void synthesizeEmptyInterfaceIfMissing(final Type type) {
         final Completer completer = type.tsym.completer;
-        if (completer != null) {
-            type.tsym.completer = new Completer() {
-                public void complete(Symbol sym) throws CompletionFailure {
-                    try {
-                        completer.complete(sym);
-                    } catch (CompletionFailure e) {
-                        sym.flags_field |= (PUBLIC | INTERFACE);
-                        ((ClassType) sym.type).supertype_field = objectType;
-                    }
+        type.tsym.completer = new Completer() {
+            public void complete(Symbol sym) throws CompletionFailure {
+                try {
+                    completer.complete(sym);
+                } catch (CompletionFailure e) {
+                    sym.flags_field |= (PUBLIC | INTERFACE);
+                    ((ClassType) sym.type).supertype_field = objectType;
                 }
-            };
-        }
+            }
+
+            @Override
+            public boolean isTerminal() {
+                return completer.isTerminal();
+            }
+        };
     }
 
     public void synthesizeBoxTypeIfMissing(final Type type) {
         ClassSymbol sym = enterClass(boxedName[type.getTag().ordinal()]);
         final Completer completer = sym.completer;
-        if (completer != null) {
-            sym.completer = new Completer() {
-                public void complete(Symbol sym) throws CompletionFailure {
-                    try {
-                        completer.complete(sym);
-                    } catch (CompletionFailure e) {
-                        sym.flags_field |= PUBLIC;
-                        ((ClassType) sym.type).supertype_field = objectType;
-                        MethodSymbol boxMethod =
-                            new MethodSymbol(PUBLIC | STATIC, names.valueOf,
-                                             new MethodType(List.of(type), sym.type,
-                                    List.<Type>nil(), methodClass),
-                                sym);
-                        sym.members().enter(boxMethod);
-                        MethodSymbol unboxMethod =
-                            new MethodSymbol(PUBLIC,
-                                type.tsym.name.append(names.Value), // x.intValue()
-                                new MethodType(List.<Type>nil(), type,
-                                    List.<Type>nil(), methodClass),
-                                sym);
-                        sym.members().enter(unboxMethod);
-                    }
+        sym.completer = new Completer() {
+            public void complete(Symbol sym) throws CompletionFailure {
+                try {
+                    completer.complete(sym);
+                } catch (CompletionFailure e) {
+                    sym.flags_field |= PUBLIC;
+                    ((ClassType) sym.type).supertype_field = objectType;
+                    MethodSymbol boxMethod =
+                        new MethodSymbol(PUBLIC | STATIC, names.valueOf,
+                                         new MethodType(List.of(type), sym.type,
+                                List.<Type>nil(), methodClass),
+                            sym);
+                    sym.members().enter(boxMethod);
+                    MethodSymbol unboxMethod =
+                        new MethodSymbol(PUBLIC,
+                            type.tsym.name.append(names.Value), // x.intValue()
+                            new MethodType(List.<Type>nil(), type,
+                                List.<Type>nil(), methodClass),
+                            sym);
+                    sym.members().enter(unboxMethod);
                 }
-            };
-        }
+            }
 
+            @Override
+            public boolean isTerminal() {
+                return completer.isTerminal();
+            }
+        };
     }
 
     // Enter a synthetic class that is used to mark classes in ct.sym.
@@ -309,7 +314,7 @@
     private Type enterSyntheticAnnotation(String name) {
         ClassType type = (ClassType)enterClass(name);
         ClassSymbol sym = (ClassSymbol)type.tsym;
-        sym.completer = null;
+        sym.completer = Completer.NULL_COMPLETER;
         sym.flags_field = PUBLIC|ACYCLIC|ANNOTATION|INTERFACE;
         sym.erasure_field = type;
         sym.members_field = WriteableScope.create(sym);
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java	Tue Apr 28 11:08:25 2015 +0300
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java	Tue Apr 28 22:25:36 2015 +0200
@@ -580,12 +580,10 @@
     }
 
     public boolean isCompound() {
-        return tsym.completer == null
-            // Compound types can't have a completer.  Calling
-            // flags() will complete the symbol causing the
-            // compiler to load classes unnecessarily.  This led
-            // to regression 6180021.
-            && (tsym.flags() & COMPOUND) != 0;
+        // Compound types can't have a (non-terminal) completer.  Calling
+        // flags() will complete the symbol causing the compiler to load
+        // classes unnecessarily.  This led to regression 6180021.
+        return tsym.isCompleted() && (tsym.flags() & COMPOUND) != 0;
     }
 
     public boolean isIntersection() {
@@ -1124,7 +1122,7 @@
         }
 
         public void complete() {
-            if (tsym.completer != null) tsym.complete();
+            tsym.complete();
         }
 
         @DefinedBy(Api.LANGUAGE_MODEL)
@@ -1212,7 +1210,7 @@
             Assert.check((csym.flags() & COMPOUND) != 0);
             supertype_field = bounds.head;
             interfaces_field = bounds.tail;
-            Assert.check(supertype_field.tsym.completer != null ||
+            Assert.check(!supertype_field.tsym.isCompleted() ||
                     !supertype_field.isInterface(), supertype_field);
         }
 
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java	Tue Apr 28 11:08:25 2015 +0300
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java	Tue Apr 28 22:25:36 2015 +0200
@@ -645,7 +645,7 @@
         Symbol descSym = findDescriptorSymbol(targets.head.tsym);
         Type descType = findDescriptorType(targets.head);
         ClassSymbol csym = new ClassSymbol(cflags, name, env.enclClass.sym.outermostClass());
-        csym.completer = null;
+        csym.completer = Completer.NULL_COMPLETER;
         csym.members_field = WriteableScope.create(csym);
         MethodSymbol instDescSym = new MethodSymbol(descSym.flags(), descSym.name, descType, csym);
         csym.members_field.enter(instDescSym);
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java	Tue Apr 28 11:08:25 2015 +0300
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java	Tue Apr 28 22:25:36 2015 +0200
@@ -1241,7 +1241,7 @@
 
         private void init() {
             // Make sure metaDataFor is member entered
-            while (metaDataFor.completer != null)
+            while (!metaDataFor.isCompleted())
                 metaDataFor.complete();
 
             if (annotationTypeCompleter != null) {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java	Tue Apr 28 11:08:25 2015 +0300
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java	Tue Apr 28 22:25:36 2015 +0200
@@ -2268,7 +2268,7 @@
             }
         }
         if (complete)
-            complete = ((c.flags_field & UNATTRIBUTED) == 0) && c.completer == null;
+            complete = ((c.flags_field & UNATTRIBUTED) == 0) && c.isCompleted();
         if (complete) c.flags_field |= ACYCLIC;
         return complete;
     }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java	Tue Apr 28 11:08:25 2015 +0300
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java	Tue Apr 28 22:25:36 2015 +0200
@@ -320,7 +320,7 @@
             ClassSymbol c = syms.enterClass(name, tree.packge);
             c.flatname = names.fromString(tree.packge + "." + name);
             c.sourcefile = tree.sourcefile;
-            c.completer = null;
+            c.completer = Completer.NULL_COMPLETER;
             c.members_field = WriteableScope.create(c);
             tree.packge.package_info = c;
         }
@@ -386,7 +386,7 @@
         typeEnvs.put(c, localEnv);
 
         // Fill out class fields.
-        c.completer = null; // do not allow the initial completer linger on.
+        c.completer = Completer.NULL_COMPLETER; // do not allow the initial completer linger on.
         c.flags_field = chk.checkFlags(tree.pos(), tree.mods.flags, c, tree);
         c.sourcefile = env.toplevel.sourcefile;
         c.members_field = WriteableScope.create(c);
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Tue Apr 28 11:08:25 2015 +0300
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java	Tue Apr 28 22:25:36 2015 +0200
@@ -641,7 +641,7 @@
             c.flatname = chk.localClassName(c);
         }
         c.sourcefile = owner.sourcefile;
-        c.completer = null;
+        c.completer = Completer.NULL_COMPLETER;
         c.members_field = WriteableScope.create(c);
         c.flags_field = flags;
         ClassType ctype = (ClassType) c.type;
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java	Tue Apr 28 11:08:25 2015 +0300
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java	Tue Apr 28 22:25:36 2015 +0200
@@ -903,7 +903,7 @@
             memberEnter.memberEnter(tree.defs, env);
 
             if (tree.sym.isAnnotationType()) {
-                Assert.checkNull(tree.sym.completer);
+                Assert.check(tree.sym.isCompleted());
                 tree.sym.setAnnotationTypeMetadata(new AnnotationTypeMetadata(tree.sym, annotate.annotationTypeSourceCompleter()));
             }
         }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java	Tue Apr 28 11:08:25 2015 +0300
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java	Tue Apr 28 22:25:36 2015 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 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
@@ -1092,7 +1092,7 @@
                     if (cs.classfile != null || cs.kind == ERR) {
                         cs.reset();
                         cs.type = new ClassType(cs.type.getEnclosingType(), null, cs);
-                        if (cs.completer == null) {
+                        if (cs.isCompleted()) {
                             cs.completer = initialCompleter;
                         }
                     }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Dependencies.java	Tue Apr 28 11:08:25 2015 +0300
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Dependencies.java	Tue Apr 28 22:25:36 2015 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 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
@@ -468,6 +468,11 @@
             sym.completer = this;
         }
 
+        @Override
+        public boolean isTerminal() {
+            return true;
+        }
+
         /**
          * This visitor is used to generate the special side-effect dependencies
          * given a graph containing only standard dependencies.
--- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/ClassDocImpl.java	Tue Apr 28 11:08:25 2015 +0300
+++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/ClassDocImpl.java	Tue Apr 28 22:25:36 2015 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -796,9 +796,7 @@
         }
 
         // make sure that this symbol has been completed
-        if (tsym.completer != null) {
-            tsym.complete();
-        }
+        tsym.complete();
 
         // search imports
 
--- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/JavadocTool.java	Tue Apr 28 11:08:25 2015 +0300
+++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/JavadocTool.java	Tue Apr 28 22:25:36 2015 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 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
@@ -40,6 +40,7 @@
 import javax.tools.StandardLocation;
 
 import com.sun.tools.javac.code.ClassFinder;
+import com.sun.tools.javac.code.Symbol.Completer;
 import com.sun.tools.javac.code.Symbol.CompletionFailure;
 import com.sun.tools.javac.comp.Enter;
 import com.sun.tools.javac.tree.JCTree;
@@ -141,7 +142,7 @@
         docenv.docClasses = docClasses;
         docenv.legacyDoclet = legacyDoclet;
 
-        javadocFinder.sourceCompleter = docClasses ? null : sourceCompleter;
+        javadocFinder.sourceCompleter = docClasses ? Completer.NULL_COMPLETER : sourceCompleter;
 
         ListBuffer<String> names = new ListBuffer<>();
         ListBuffer<JCCompilationUnit> classTrees = new ListBuffer<>();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/completionDeps/DepsAndAnno.java	Tue Apr 28 22:25:36 2015 +0200
@@ -0,0 +1,49 @@
+/*
+ * 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 8078600
+ * @summary Make sure -XDcompletionDeps does not cause an infinite loop.
+ * @library /tools/lib
+ * @build ToolBox
+ * @run main/othervm/timeout=10 DepsAndAnno
+ */
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+public class DepsAndAnno {
+
+    @Target(ElementType.METHOD)
+    @interface Test { }
+
+    public static void main(String[] args) {
+        ToolBox toolBox = new ToolBox();
+        toolBox.new JavacTask(ToolBox.Mode.CMDLINE)
+               .options("-XDcompletionDeps")
+               .outdir(".")
+               .files(ToolBox.testSrc + "/DepsAndAnno.java")
+               .run();
+    }
+}