8078600: Infinite loop when compiling annotations with -XDcompletionDeps

Added Completer::isTerminal and added NULL_COMPLETER.

Reviewed-by: jlahoda, mcimadamore
This commit is contained in:
Andreas Lundblad 2015-04-28 22:25:36 +02:00
parent 153dc079bb
commit 4c307784ae
15 changed files with 158 additions and 75 deletions

View File

@ -128,7 +128,7 @@ public class ClassFinder {
* 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 @@ public class ClassFinder {
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 class ClassFinder {
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) {

View File

@ -96,6 +96,7 @@ public abstract class Symbol extends AnnoConstruct implements Element {
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 @@ public abstract class Symbol extends AnnoConstruct implements Element {
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 @@ public abstract class Symbol extends AnnoConstruct implements Element {
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 @@ public abstract class Symbol extends AnnoConstruct implements Element {
/** 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 abstract class Symbol extends AnnoConstruct implements Element {
}
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 abstract class Symbol extends AnnoConstruct implements Element {
}
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 @@ public abstract class Symbol extends AnnoConstruct implements Element {
/** 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 {

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
@ -259,49 +259,54 @@ public class Symtab {
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 @@ public class Symtab {
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);

View File

@ -580,12 +580,10 @@ public abstract class Type extends AnnoConstruct implements TypeMirror {
}
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 abstract class Type extends AnnoConstruct implements TypeMirror {
}
public void complete() {
if (tsym.completer != null) tsym.complete();
tsym.complete();
}
@DefinedBy(Api.LANGUAGE_MODEL)
@ -1212,7 +1210,7 @@ public abstract class Type extends AnnoConstruct implements TypeMirror {
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);
}

View File

@ -645,7 +645,7 @@ public class Types {
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);

View File

@ -1241,7 +1241,7 @@ public class Annotate {
private void init() {
// Make sure metaDataFor is member entered
while (metaDataFor.completer != null)
while (!metaDataFor.isCompleted())
metaDataFor.complete();
if (annotationTypeCompleter != null) {

View File

@ -2268,7 +2268,7 @@ public class Check {
}
}
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;
}

View File

@ -320,7 +320,7 @@ public class Enter extends JCTree.Visitor {
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 @@ public class Enter extends JCTree.Visitor {
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);

View File

@ -641,7 +641,7 @@ public class Lower extends TreeTranslator {
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;

View File

@ -903,7 +903,7 @@ public class TypeEnter implements Completer {
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()));
}
}

View File

@ -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 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
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;
}
}

View File

@ -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 @@ public abstract class Dependencies {
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.

View File

@ -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 @@ public class ClassDocImpl extends ProgramElementDocImpl implements ClassDoc {
}
// make sure that this symbol has been completed
if (tsym.completer != null) {
tsym.complete();
}
tsym.complete();
// search imports

View File

@ -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.StandardJavaFileManager;
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 @@ public class JavadocTool extends com.sun.tools.javac.main.JavaCompiler {
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<>();

View File

@ -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();
}
}