8078600: Infinite loop when compiling annotations with -XDcompletionDeps
Summary: Added Completer::isTerminal and added NULL_COMPLETER.
Reviewed-by: jlahoda, mcimadamore
--- 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();
+ }
+}