8187950: javax.lang.model APIs throws CompletionFailure or a subtype of CompletionFailure

Catching CompletionFailures that would be thrown to API clients, and re-completing the symbols again when javac itself needs it.

Reviewed-by: cushon, jjg
This commit is contained in:
Jan Lahoda 2018-03-09 09:42:10 +01:00
parent 798b830ad8
commit b0b8a51be1
22 changed files with 1010 additions and 80 deletions

@ -30,11 +30,14 @@ import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import com.sun.tools.javac.code.Kinds.Kind;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.comp.AttrContext;
import com.sun.tools.javac.comp.Env;
import com.sun.tools.javac.util.DefinedBy;
import com.sun.tools.javac.util.DefinedBy.Api;
import com.sun.tools.javac.util.Assert;
import com.sun.tools.javac.util.Filter;
/**
* Provides an implementation of Scope.
@ -48,6 +51,11 @@ import com.sun.tools.javac.util.Assert;
*/
public class JavacScope implements com.sun.source.tree.Scope {
private static final Filter<Symbol> VALIDATOR = sym -> {
sym.apiComplete();
return sym.kind != Kind.ERR;
};
static JavacScope create(Env<AttrContext> env) {
if (env.outer == null || env.outer == env) {
//the "top-level" scope needs to return both imported and defined elements
@ -55,7 +63,7 @@ public class JavacScope implements com.sun.source.tree.Scope {
return new JavacScope(env) {
@Override @DefinedBy(Api.COMPILER_TREE)
public Iterable<? extends Element> getLocalElements() {
return env.toplevel.namedImportScope.getSymbols();
return env.toplevel.namedImportScope.getSymbols(VALIDATOR);
}
};
} else {
@ -85,7 +93,7 @@ public class JavacScope implements com.sun.source.tree.Scope {
}
@DefinedBy(Api.COMPILER_TREE)
public Iterable<? extends Element> getLocalElements() {
return env.toplevel.starImportScope.getSymbols();
return env.toplevel.starImportScope.getSymbols(VALIDATOR);
}
};
}

@ -38,6 +38,7 @@ import javax.tools.*;
import com.sun.source.tree.*;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.DeferredCompletionFailureHandler.Handler;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.comp.*;
import com.sun.tools.javac.file.BaseFileManager;
@ -72,6 +73,7 @@ public class JavacTaskImpl extends BasicJavacTask {
private final Arguments args;
private JavaCompiler compiler;
private JavaFileManager fileManager;
private DeferredCompletionFailureHandler dcfh;
private Locale locale;
private Map<JavaFileObject, JCCompilationUnit> notYetEntered;
private ListBuffer<Env<AttrContext>> genList;
@ -83,6 +85,8 @@ public class JavacTaskImpl extends BasicJavacTask {
super(context, true);
args = Arguments.instance(context);
fileManager = context.get(JavaFileManager.class);
dcfh = DeferredCompletionFailureHandler.instance(context);
dcfh.setHandler(dcfh.userCodeHandler);
}
@Override @DefinedBy(Api.COMPILER)
@ -138,6 +142,7 @@ public class JavacTaskImpl extends BasicJavacTask {
}
private <T> T handleExceptions(Callable<T> c, T sysErrorResult, T abnormalErrorResult) {
Handler prevDeferredHandler = dcfh.setHandler(dcfh.javacCodeHandler);
try {
return c.call();
} catch (FatalError ex) {
@ -171,6 +176,8 @@ public class JavacTaskImpl extends BasicJavacTask {
ex.printStackTrace(log.getWriter(WriterKind.NOTICE));
}
return abnormalErrorResult;
} finally {
dcfh.setHandler(prevDeferredHandler);
}
}

@ -30,7 +30,9 @@ import java.io.IOException;
import java.text.BreakIterator;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -180,6 +182,8 @@ public class JavacTrees extends DocTrees {
private ParserFactory parser;
private Symtab syms;
private final Map<Type, Type> extraType2OriginalMap = new WeakHashMap<>();
// called reflectively from Trees.instance(CompilationTask task)
public static JavacTrees instance(JavaCompiler.CompilationTask task) {
if (!(task instanceof BasicJavacTask))
@ -1083,6 +1087,20 @@ public class JavacTrees extends DocTrees {
if (errorType instanceof com.sun.tools.javac.code.Type.ErrorType) {
return ((com.sun.tools.javac.code.Type.ErrorType)errorType).getOriginalType();
}
if (errorType instanceof com.sun.tools.javac.code.Type.ClassType &&
errorType.getKind() == TypeKind.ERROR) {
ClassType ct = (ClassType) errorType;
return extraType2OriginalMap.computeIfAbsent(ct, tt ->
new ClassType(ct.getEnclosingType(), ct.typarams_field,
ct.tsym, ct.getMetadata()) {
@Override
public Type baseType() { return ct; }
@Override
public TypeKind getKind() {
return TypeKind.DECLARED;
}
});
}
return com.sun.tools.javac.code.Type.noType;
}

@ -30,6 +30,8 @@ import java.util.Collection;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskListener;
import com.sun.tools.javac.code.DeferredCompletionFailureHandler;
import com.sun.tools.javac.code.DeferredCompletionFailureHandler.Handler;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.DefinedBy;
import com.sun.tools.javac.util.DefinedBy.Api;
@ -50,6 +52,8 @@ public class MultiTaskListener implements TaskListener {
/** Empty array of task listeners */
private static final TaskListener[] EMPTY_LISTENERS = new TaskListener[0];
private final DeferredCompletionFailureHandler dcfh;
/** Get the MultiTaskListener instance for this context. */
public static MultiTaskListener instance(Context context) {
MultiTaskListener instance = context.get(taskListenerKey);
@ -61,6 +65,7 @@ public class MultiTaskListener implements TaskListener {
protected MultiTaskListener(Context context) {
context.put(taskListenerKey, this);
ccw = ClientCodeWrapper.instance(context);
dcfh = DeferredCompletionFailureHandler.instance(context);
}
/**
@ -106,18 +111,28 @@ public class MultiTaskListener implements TaskListener {
@Override @DefinedBy(Api.COMPILER_TREE)
public void started(TaskEvent e) {
// guard against listeners being updated by a listener
TaskListener[] ll = this.listeners;
for (TaskListener l: ll)
l.started(e);
Handler prevDeferredHandler = dcfh.setHandler(dcfh.userCodeHandler);
try {
// guard against listeners being updated by a listener
TaskListener[] ll = this.listeners;
for (TaskListener l: ll)
l.started(e);
} finally {
dcfh.setHandler(prevDeferredHandler);
}
}
@Override @DefinedBy(Api.COMPILER_TREE)
public void finished(TaskEvent e) {
// guard against listeners being updated by a listener
TaskListener[] ll = this.listeners;
for (TaskListener l: ll)
l.finished(e);
Handler prevDeferredHandler = dcfh.setHandler(dcfh.userCodeHandler);
try {
// guard against listeners being updated by a listener
TaskListener[] ll = this.listeners;
for (TaskListener l: ll)
l.finished(e);
} finally {
dcfh.setHandler(prevDeferredHandler);
}
}
@Override

@ -63,6 +63,8 @@ import static javax.tools.StandardLocation.*;
import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Kinds.Kind.*;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.CompletionFailure;
import com.sun.tools.javac.main.DelegatingJavaFileManager;
import com.sun.tools.javac.util.Dependencies.CompletionCause;
@ -131,6 +133,8 @@ public class ClassFinder {
*/
JCDiagnostic.Factory diagFactory;
final DeferredCompletionFailureHandler dcfh;
/** Can be reassigned from outside:
* the completer to be used for ".java" files. If this remains unassigned
* ".java" files will not be loaded.
@ -185,6 +189,7 @@ public class ClassFinder {
if (fileManager == null)
throw new AssertionError("FileManager initialization error");
diagFactory = JCDiagnostic.Factory.instance(context);
dcfh = DeferredCompletionFailureHandler.instance(context);
log = Log.instance(context);
annotate = Annotate.instance(context);
@ -217,6 +222,8 @@ public class ClassFinder {
jrtIndex = useCtProps && JRTIndex.isAvailable() ? JRTIndex.getSharedInstance() : null;
profile = Profile.instance(context);
cachedCompletionFailure = new CompletionFailure(null, (JCDiagnostic) null, dcfh);
cachedCompletionFailure.setStackTrace(new StackTraceElement[0]);
}
@ -293,7 +300,7 @@ public class ClassFinder {
} catch (IOException ex) {
JCDiagnostic msg =
diagFactory.fragment(Fragments.ExceptionMessage(ex.getLocalizedMessage()));
throw new CompletionFailure(sym, msg).initCause(ex);
throw new CompletionFailure(sym, msg, dcfh).initCause(ex);
}
}
if (!reader.filling)
@ -332,7 +339,7 @@ public class ClassFinder {
if (completionFailureName == c.fullname) {
JCDiagnostic msg =
diagFactory.fragment(Fragments.UserSelectedCompletionFailure);
throw new CompletionFailure(c, msg);
throw new CompletionFailure(c, msg, dcfh);
}
currentOwner = c;
JavaFileObject classfile = c.classfile;
@ -397,7 +404,7 @@ public class ClassFinder {
// log.warning("proc.messager",
// Log.getLocalizedString("class.file.not.found", c.flatname));
// c.debug.printStackTrace();
return new CompletionFailure(c, diag);
return new CompletionFailure(c, diag, dcfh);
} else {
CompletionFailure result = cachedCompletionFailure;
result.sym = c;
@ -405,11 +412,7 @@ public class ClassFinder {
return result;
}
}
private final CompletionFailure cachedCompletionFailure =
new CompletionFailure(null, (JCDiagnostic) null);
{
cachedCompletionFailure.setStackTrace(new StackTraceElement[0]);
}
private final CompletionFailure cachedCompletionFailure;
/** Load a toplevel class with given fully qualified name
@ -775,8 +778,8 @@ public class ClassFinder {
private static final long serialVersionUID = 0;
public BadClassFile(TypeSymbol sym, JavaFileObject file, JCDiagnostic diag,
JCDiagnostic.Factory diagFactory) {
super(sym, createBadClassFileDiagnostic(file, diag, diagFactory));
JCDiagnostic.Factory diagFactory, DeferredCompletionFailureHandler dcfh) {
super(sym, createBadClassFileDiagnostic(file, diag, diagFactory), dcfh);
}
// where
private static JCDiagnostic createBadClassFileDiagnostic(
@ -791,8 +794,8 @@ public class ClassFinder {
private static final long serialVersionUID = 0;
public BadEnclosingMethodAttr(TypeSymbol sym, JavaFileObject file, JCDiagnostic diag,
JCDiagnostic.Factory diagFactory) {
super(sym, file, diag, diagFactory);
JCDiagnostic.Factory diagFactory, DeferredCompletionFailureHandler dcfh) {
super(sym, file, diag, diagFactory, dcfh);
}
}
}

@ -0,0 +1,177 @@
/*
* Copyright (c) 2017, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package com.sun.tools.javac.code;
import java.util.Map;
import java.util.WeakHashMap;
import com.sun.tools.javac.code.Kinds.Kind;
import com.sun.tools.javac.code.Scope.WriteableScope;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.Completer;
import com.sun.tools.javac.code.Symbol.CompletionFailure;
import com.sun.tools.javac.util.Context;
/** When a CompletionFailure is thrown when user code is running, it shouldn't be
* thrown out to the client code, but rather skipped, and then rethrown later if javac
* itself will complete the Symbol.
*
* On all places where javac invokes client code (e.g. TaskListeners, annotation
* Processors), the {@code userCodeHandler} should be set using
* {@link DeferredCompletionFailureHandler#setHandler}, and the original handler
* should be restored when the control returns back to javac.
*
* Implementations of API methods should use {@link Symbol#apiComplete()} instead of
* {@link Symbol#complete}, as the {@code apiComplete} method will invoke
* {@link DeferredCompletionFailureHandler#handleAPICompletionFailure }, which will
* catch the CompletionFailure and will either rethrow it or skip it, depending on
* the context.
*/
public class DeferredCompletionFailureHandler {
protected static final Context.Key<DeferredCompletionFailureHandler> deferredCompletionFailureHandlerKey = new Context.Key<>();
public static DeferredCompletionFailureHandler instance(Context context) {
DeferredCompletionFailureHandler instance = context.get(deferredCompletionFailureHandlerKey);
if (instance == null)
instance = new DeferredCompletionFailureHandler(context);
return instance;
}
public final Handler userCodeHandler = new Handler() {
private final Map<ClassSymbol, FlipSymbolDescription> class2Flip = new WeakHashMap<>();
public void install() {
class2Flip.values().forEach(f -> f.flip());
}
public void handleAPICompletionFailure(CompletionFailure cf) {
//ignore
}
public void classSymbolCompleteFailed(ClassSymbol sym, Completer origCompleter) {
class2Flip.put(sym, new FlipSymbolDescription(sym, new DeferredCompleter(origCompleter) {
@Override public void complete(Symbol sym) throws CompletionFailure {
class2Flip.remove(sym);
super.complete(sym);
}
}));
}
public void uninstall() {
class2Flip.values().forEach(f -> f.flip());
}
};
public final Handler javacCodeHandler = new Handler() {
public void install() {
}
public void handleAPICompletionFailure(CompletionFailure cf) {
throw cf;
}
public void classSymbolCompleteFailed(ClassSymbol sym, Completer origCompleter) {}
public void uninstall() {
}
};
private Handler handler = javacCodeHandler;
protected DeferredCompletionFailureHandler(Context context) {
context.put(deferredCompletionFailureHandlerKey, this);
}
public Handler setHandler(Handler h) {
if (h == handler) return handler;
handler.uninstall();
Handler prev = handler;
handler = h;
handler.install();
return prev;
}
public void handleAPICompletionFailure(CompletionFailure cf) {
handler.handleAPICompletionFailure(cf);
}
public void classSymbolCompleteFailed(ClassSymbol sym, Completer origCompleter) {
handler.classSymbolCompleteFailed(sym, origCompleter);
}
public boolean isDeferredCompleter(Completer c) {
return c instanceof DeferredCompleter;
}
public interface Handler {
public void install();
public void handleAPICompletionFailure(CompletionFailure cf);
public void classSymbolCompleteFailed(ClassSymbol sym, Completer origCompleter);
public void uninstall();
}
private class DeferredCompleter implements Completer {
private final Completer origCompleter;
public DeferredCompleter(Completer origCompleter) {
this.origCompleter = origCompleter;
}
@Override
public void complete(Symbol sym) throws CompletionFailure {
origCompleter.complete(sym);
}
}
private static class FlipSymbolDescription {
public final ClassSymbol sym;
public Type type;
public Kind kind;
public WriteableScope members;
public Completer completer;
public FlipSymbolDescription(ClassSymbol sym, Completer completer) {
this.sym = sym;
this.type = sym.type;
this.kind = sym.kind;
this.members = null;
this.completer = completer;
}
public void flip() {
Type prevType = sym.type;
sym.type = type;
this.type = prevType;
Kind prevKind = sym.kind;
sym.kind = kind;
this.kind = prevKind;
Completer prevCompleter = sym.completer;
sym.completer = completer;
this.completer = prevCompleter;
WriteableScope prevMembers = sym.members_field;
sym.members_field = members;
this.members = prevMembers;
}
}
}

@ -89,6 +89,8 @@ public class ModuleFinder {
private final JCDiagnostic.Factory diags;
private final DeferredCompletionFailureHandler dcfh;
private ModuleNameReader moduleNameReader;
public ModuleNameFromSourceReader moduleNameFromSourceReader;
@ -111,6 +113,7 @@ public class ModuleFinder {
classFinder = ClassFinder.instance(context);
diags = JCDiagnostic.Factory.instance(context);
dcfh = DeferredCompletionFailureHandler.instance(context);
}
class ModuleLocationIterator implements Iterator<Set<Location>> {
@ -227,7 +230,7 @@ public class ModuleFinder {
JCDiagnostic diag =
diags.fragment(Fragments.FileDoesNotContainModule);
ClassSymbol errModuleInfo = syms.defineClass(names.module_info, syms.errModule);
throw new ClassFinder.BadClassFile(errModuleInfo, fo, diag, diags);
throw new ClassFinder.BadClassFile(errModuleInfo, fo, diag, diags, dcfh);
}
break;
case CLASS:

@ -47,11 +47,8 @@ import javax.lang.model.element.VariableElement;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import com.sun.tools.javac.code.ClassFinder.BadEnclosingMethodAttr;
import com.sun.tools.javac.code.Directive.RequiresFlag;
import com.sun.tools.javac.code.Kinds.Kind;
import com.sun.tools.javac.comp.Annotate.AnnotationTypeMetadata;
import com.sun.tools.javac.code.Scope.WriteableScope;
import com.sun.tools.javac.code.Type.*;
import com.sun.tools.javac.comp.Attr;
import com.sun.tools.javac.comp.AttrContext;
@ -68,6 +65,7 @@ import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Kinds.*;
import static com.sun.tools.javac.code.Kinds.Kind.*;
import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
import com.sun.tools.javac.code.Scope.WriteableScope;
import static com.sun.tools.javac.code.Symbol.OperatorSymbol.AccessCode.FIRSTASGOP;
import static com.sun.tools.javac.code.TypeTag.CLASS;
import static com.sun.tools.javac.code.TypeTag.FORALL;
@ -645,6 +643,14 @@ public abstract class Symbol extends AnnoConstruct implements Element {
}
}
public void apiComplete() throws CompletionFailure {
try {
complete();
} catch (CompletionFailure cf) {
cf.dcfh.handleAPICompletionFailure(cf);
}
}
/** True if the symbol represents an entity that exists.
*/
public boolean exists() {
@ -668,6 +674,7 @@ public abstract class Symbol extends AnnoConstruct implements Element {
@DefinedBy(Api.LANGUAGE_MODEL)
public Set<Modifier> getModifiers() {
apiComplete();
return Flags.asModifierSet(flags());
}
@ -682,6 +689,7 @@ public abstract class Symbol extends AnnoConstruct implements Element {
*/
@Override @DefinedBy(Api.LANGUAGE_MODEL)
public List<Attribute.Compound> getAnnotationMirrors() {
apiComplete();
return getRawAttributes();
}
@ -806,13 +814,11 @@ public abstract class Symbol extends AnnoConstruct implements Element {
if (kind == TYP && type.hasTag(TYPEVAR)) {
return list;
}
apiComplete();
for (Symbol sym : members().getSymbols(NON_RECURSIVE)) {
try {
if (sym != null && (sym.flags() & SYNTHETIC) == 0 && sym.owner == this) {
list = list.prepend(sym);
}
} catch (BadEnclosingMethodAttr badEnclosingMethod) {
// ignore the exception
sym.apiComplete();
if ((sym.flags() & SYNTHETIC) == 0 && sym.owner == this && sym.kind != ERR) {
list = list.prepend(sym);
}
}
return list;
@ -991,7 +997,7 @@ public abstract class Symbol extends AnnoConstruct implements Element {
@Override @DefinedBy(Api.LANGUAGE_MODEL)
public java.util.List<Directive> getDirectives() {
complete();
apiComplete();
completeUsesProvides();
return Collections.unmodifiableList(directives);
}
@ -1315,9 +1321,11 @@ public abstract class Symbol extends AnnoConstruct implements Element {
/** Complete the elaboration of this symbol's definition.
*/
public void complete() throws CompletionFailure {
Completer origCompleter = completer;
try {
super.complete();
} catch (CompletionFailure ex) {
ex.dcfh.classSymbolCompleteFailed(this, origCompleter);
// quiet error recovery
flags_field |= (PUBLIC|STATIC);
this.type = new ErrorType(this, Type.noType);
@ -1327,7 +1335,7 @@ public abstract class Symbol extends AnnoConstruct implements Element {
@DefinedBy(Api.LANGUAGE_MODEL)
public List<Type> getInterfaces() {
complete();
apiComplete();
if (type instanceof ClassType) {
ClassType t = (ClassType)type;
if (t.interfaces_field == null) // FIXME: shouldn't be null
@ -1342,7 +1350,7 @@ public abstract class Symbol extends AnnoConstruct implements Element {
@DefinedBy(Api.LANGUAGE_MODEL)
public Type getSuperclass() {
complete();
apiComplete();
if (type instanceof ClassType) {
ClassType t = (ClassType)type;
if (t.supertype_field == null) // FIXME: shouldn't be null
@ -1383,6 +1391,7 @@ public abstract class Symbol extends AnnoConstruct implements Element {
@DefinedBy(Api.LANGUAGE_MODEL)
public ElementKind getKind() {
apiComplete();
long flags = flags();
if ((flags & ANNOTATION) != 0)
return ElementKind.ANNOTATION_TYPE;
@ -1396,13 +1405,14 @@ public abstract class Symbol extends AnnoConstruct implements Element {
@Override @DefinedBy(Api.LANGUAGE_MODEL)
public Set<Modifier> getModifiers() {
apiComplete();
long flags = flags();
return Flags.asModifierSet(flags & ~DEFAULT);
}
@DefinedBy(Api.LANGUAGE_MODEL)
public NestingKind getNestingKind() {
complete();
apiComplete();
if (owner.kind == PCK)
return NestingKind.TOP_LEVEL;
else if (name.isEmpty())
@ -2116,13 +2126,15 @@ public abstract class Symbol extends AnnoConstruct implements Element {
public static class CompletionFailure extends RuntimeException {
private static final long serialVersionUID = 0;
public final DeferredCompletionFailureHandler dcfh;
public Symbol sym;
/** A diagnostic object describing the failure
*/
public JCDiagnostic diag;
public CompletionFailure(Symbol sym, JCDiagnostic diag) {
public CompletionFailure(Symbol sym, JCDiagnostic diag, DeferredCompletionFailureHandler dcfh) {
this.dcfh = dcfh;
this.sym = sym;
this.diag = diag;
// this.printStackTrace();//DEBUG

@ -930,7 +930,8 @@ public abstract class Type extends AnnoConstruct implements TypeMirror {
}
}
public static class ClassType extends Type implements DeclaredType {
public static class ClassType extends Type implements DeclaredType,
javax.lang.model.type.ErrorType {
/** The enclosing type of this type. If this is the type of an inner
* class, outer_field refers to the type of its enclosing
@ -1141,7 +1142,8 @@ public abstract class Type extends AnnoConstruct implements TypeMirror {
@DefinedBy(Api.LANGUAGE_MODEL)
public TypeKind getKind() {
return TypeKind.DECLARED;
tsym.apiComplete();
return tsym.kind == TYP ? TypeKind.DECLARED : TypeKind.ERROR;
}
@DefinedBy(Api.LANGUAGE_MODEL)

@ -4178,9 +4178,6 @@ public class Resolve {
Name name,
List<Type> argtypes,
List<Type> typeargtypes) {
if (sym.owner.type.hasTag(ERROR))
return null;
if (sym.name == names.init && sym.owner != site.tsym) {
return new SymbolNotFoundError(ABSENT_MTH).getDiagnostic(dkind,
pos, location, site, name, argtypes, typeargtypes);

@ -146,6 +146,8 @@ public class ClassReader {
*/
JCDiagnostic.Factory diagFactory;
DeferredCompletionFailureHandler dcfh;
/** The current scope where type variables are entered.
*/
protected WriteableScope typevars;
@ -260,6 +262,7 @@ public class ClassReader {
if (fileManager == null)
throw new AssertionError("FileManager initialization error");
diagFactory = JCDiagnostic.Factory.instance(context);
dcfh = DeferredCompletionFailureHandler.instance(context);
log = Log.instance(context);
@ -299,7 +302,8 @@ public class ClassReader {
currentOwner.enclClass(),
currentClassFile,
diagFactory.fragment(key, args),
diagFactory);
diagFactory,
dcfh);
}
public ClassFinder.BadEnclosingMethodAttr badEnclosingMethod(Symbol sym) {
@ -307,7 +311,8 @@ public class ClassReader {
currentOwner.enclClass(),
currentClassFile,
diagFactory.fragment(Fragments.BadEnclosingMethod(sym)),
diagFactory);
diagFactory,
dcfh);
}
/************************************************************************
@ -2663,7 +2668,7 @@ public class ClassReader {
long f = nextChar();
long flags = adjustClassFlags(f);
if ((flags & MODULE) == 0) {
if (c.owner.kind == PCK) c.flags_field = flags;
if (c.owner.kind == PCK || c.owner.kind == ERR) c.flags_field = flags;
// read own class name and check that it matches
currentModule = c.packge().modle;
ClassSymbol self = readClassSymbol(nextChar());
@ -3065,7 +3070,9 @@ public class ClassReader {
theRepeatable = deproxy.deproxyCompound(repeatable);
}
} catch (Exception e) {
throw new CompletionFailure(sym, ClassReader.this.diagFactory.fragment(Fragments.ExceptionMessage(e.getMessage())));
throw new CompletionFailure(sym,
ClassReader.this.diagFactory.fragment(Fragments.ExceptionMessage(e.getMessage())),
dcfh);
}
sym.getAnnotationTypeMetadata().setTarget(theTarget);

@ -311,6 +311,8 @@ public class JavaCompiler {
*/
protected JCDiagnostic.Factory diags;
protected DeferredCompletionFailureHandler dcfh;
/** The type eraser.
*/
protected TransTypes transTypes;
@ -416,6 +418,7 @@ public class JavaCompiler {
modules = Modules.instance(context);
moduleFinder = ModuleFinder.instance(context);
diags = Factory.instance(context);
dcfh = DeferredCompletionFailureHandler.instance(context);
finder.sourceCompleter = sourceCompleter;
modules.findPackageInFile = this::findPackageInFile;
@ -799,7 +802,7 @@ public class JavaCompiler {
if (completionFailureName == c.fullname) {
JCDiagnostic msg =
diagFactory.fragment(Fragments.UserSelectedCompletionFailure);
throw new CompletionFailure(c, msg);
throw new CompletionFailure(c, msg, dcfh);
}
JavaFileObject filename = c.classfile;
JavaFileObject prev = log.useSource(filename);
@ -827,7 +830,7 @@ public class JavaCompiler {
// have enough modules available to access java.lang, and
// so risk getting FatalError("no.java.lang") from MemberEnter.
if (!modules.enter(List.of(tree), c)) {
throw new CompletionFailure(c, diags.fragment(Fragments.CantResolveModules));
throw new CompletionFailure(c, diags.fragment(Fragments.CantResolveModules), dcfh);
}
enter.complete(List.of(tree), c);
@ -848,18 +851,18 @@ public class JavaCompiler {
if (enter.getEnv(tree.modle) == null) {
JCDiagnostic diag =
diagFactory.fragment(Fragments.FileDoesNotContainModule);
throw new ClassFinder.BadClassFile(c, filename, diag, diagFactory);
throw new ClassFinder.BadClassFile(c, filename, diag, diagFactory, dcfh);
}
} else if (isPkgInfo) {
if (enter.getEnv(tree.packge) == null) {
JCDiagnostic diag =
diagFactory.fragment(Fragments.FileDoesNotContainPackage(c.location()));
throw new ClassFinder.BadClassFile(c, filename, diag, diagFactory);
throw new ClassFinder.BadClassFile(c, filename, diag, diagFactory, dcfh);
}
} else {
JCDiagnostic diag =
diagFactory.fragment(Fragments.FileDoesntContainClass(c.getQualifiedName()));
throw new ClassFinder.BadClassFile(c, filename, diag, diagFactory);
throw new ClassFinder.BadClassFile(c, filename, diag, diagFactory, dcfh);
}
}

@ -255,7 +255,8 @@ public class JavacElements implements Elements {
name.equals(sym.getQualifiedName()))
? clazz.cast(sym)
: null;
} catch (CompletionFailure e) {
} catch (CompletionFailure cf) {
cf.dcfh.handleAPICompletionFailure(cf);
return null;
}
}
@ -442,7 +443,7 @@ public class JavacElements implements Elements {
@DefinedBy(Api.LANGUAGE_MODEL)
public boolean isDeprecated(Element e) {
Symbol sym = cast(Symbol.class, e);
sym.complete();
sym.apiComplete();
return sym.isDeprecated();
}

@ -52,6 +52,7 @@ import static javax.tools.StandardLocation.*;
import com.sun.source.util.TaskEvent;
import com.sun.tools.javac.api.MultiTaskListener;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.DeferredCompletionFailureHandler.Handler;
import com.sun.tools.javac.code.Scope.WriteableScope;
import com.sun.tools.javac.code.Source.Feature;
import com.sun.tools.javac.code.Symbol.*;
@ -177,6 +178,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
private MultiTaskListener taskListener;
private final Symtab symtab;
private final DeferredCompletionFailureHandler dcfh;
private final Names names;
private final Enter enter;
private final Completer initialCompleter;
@ -227,6 +229,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
messages = JavacMessages.instance(context);
taskListener = MultiTaskListener.instance(context);
symtab = Symtab.instance(context);
dcfh = DeferredCompletionFailureHandler.instance(context);
names = Names.instance(context);
enter = Enter.instance(context);
initialCompleter = ClassFinder.instance(context).getCompleter();
@ -665,10 +668,12 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
private ArrayList<Pattern> supportedAnnotationPatterns;
private ArrayList<String> supportedOptionNames;
ProcessorState(Processor p, Log log, Source source, boolean allowModules, ProcessingEnvironment env) {
ProcessorState(Processor p, Log log, Source source, DeferredCompletionFailureHandler dcfh,
boolean allowModules, ProcessingEnvironment env) {
processor = p;
contributed = false;
Handler prevDeferredHandler = dcfh.setHandler(dcfh.userCodeHandler);
try {
processor.init(env);
@ -692,6 +697,8 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
throw e;
} catch (Throwable t) {
throw new AnnotationProcessingError(t);
} finally {
dcfh.setHandler(prevDeferredHandler);
}
}
@ -767,7 +774,8 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
if (psi.processorIterator.hasNext()) {
ProcessorState ps = new ProcessorState(psi.processorIterator.next(),
log, source, Feature.MODULES.allowedInSource(source),
log, source, dcfh,
Feature.MODULES.allowedInSource(source),
JavacProcessingEnvironment.this);
psi.procStateList.add(ps);
return ps;
@ -959,6 +967,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
private boolean callProcessor(Processor proc,
Set<? extends TypeElement> tes,
RoundEnvironment renv) {
Handler prevDeferredHandler = dcfh.setHandler(dcfh.userCodeHandler);
try {
return proc.process(tes, renv);
} catch (ClassFinder.BadClassFile ex) {
@ -973,6 +982,8 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
throw e;
} catch (Throwable t) {
throw new AnnotationProcessingError(t);
} finally {
dcfh.setHandler(prevDeferredHandler);
}
}

@ -976,8 +976,7 @@ public class ElementsTable {
*/
private void addAllClasses(Collection<TypeElement> list, PackageElement pkg) {
boolean filtered = true;
PackageSymbol sym = (PackageSymbol)pkg;
for (Symbol isym : sym.members().getSymbols(NON_RECURSIVE)) {
for (Element isym : pkg.getEnclosedElements()) {
addAllClasses(list, (TypeElement)isym, filtered);
}
}

@ -38,6 +38,7 @@ import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import com.sun.tools.javac.code.ClassFinder;
import com.sun.tools.javac.code.DeferredCompletionFailureHandler;
import com.sun.tools.javac.code.Symbol.Completer;
import com.sun.tools.javac.code.Symbol.CompletionFailure;
import com.sun.tools.javac.comp.Enter;
@ -70,6 +71,7 @@ public class JavadocTool extends com.sun.tools.javac.main.JavaCompiler {
final Messager messager;
final ClassFinder javadocFinder;
final DeferredCompletionFailureHandler dcfh;
final Enter javadocEnter;
final Set<JavaFileObject> uniquefiles;
@ -81,6 +83,7 @@ public class JavadocTool extends com.sun.tools.javac.main.JavaCompiler {
super(context);
messager = Messager.instance0(context);
javadocFinder = JavadocClassFinder.instance(context);
dcfh = DeferredCompletionFailureHandler.instance(context);
javadocEnter = JavadocEnter.instance(context);
uniquefiles = new HashSet<>();
}
@ -208,6 +211,7 @@ public class JavadocTool extends com.sun.tools.javac.main.JavaCompiler {
etable.setClassDeclList(listClasses(classTrees.toList()));
dcfh.setHandler(dcfh.userCodeHandler);
etable.analyze();
} catch (CompletionFailure cf) {
throw new ToolException(ABNORMAL, cf.getMessage(), cf);

@ -63,6 +63,8 @@ public class T7031108 extends JavacTestingAbstractProcessor {
+ " }\n"
+ "}");
private static final String PACKAGE_CONTENT_ERROR = "package does not contain C";
/* Dummy source file to compile while running anno processor. */
static final JavaSource dummy =
new JavaSource("Dummy.java",
@ -96,10 +98,15 @@ public class T7031108 extends JavacTestingAbstractProcessor {
throw new Exception("no diagnostics received");
case 1:
String code = diags.get(0).getCode();
String expect = "compiler.err.proc.cant.access.1";
String expect = "compiler.err.proc.messager";
if (!expect.equals(code))
throw new Exception("unexpected diag code: " + code
+ ", expected: " + expect);
String message = diags.get(0).getMessage(null);
if (!PACKAGE_CONTENT_ERROR.equals(message)) {
throw new Exception("unexpected diag message: " + code
+ ", expected: " + PACKAGE_CONTENT_ERROR);
}
break;
default:
throw new Exception("unexpected diags received");
@ -143,7 +150,7 @@ public class T7031108 extends JavacTestingAbstractProcessor {
List<? extends Element> elems = p.getEnclosedElements();
System.err.println("contents of package p: " + elems);
if (elems.size() != 1 || !elems.get(0).getSimpleName().contentEquals("C")) {
messager.printMessage(Diagnostic.Kind.ERROR, "unexpected package contents");
messager.printMessage(Diagnostic.Kind.ERROR, PACKAGE_CONTENT_ERROR);
}
}
return true;

@ -37,9 +37,6 @@
import java.io.*;
import java.util.*;
import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.lang.model.element.*;
import javax.tools.*;
import com.sun.source.util.*;
import com.sun.tools.javac.api.*;
@ -59,32 +56,25 @@ public class T6430209 {
String testSrc = System.getProperty("test.src", ".");
String testClassPath = System.getProperty("test.class.path");
JavacTool tool = JavacTool.create();
MyDiagListener dl = new MyDiagListener();
try (StandardJavaFileManager fm = tool.getStandardFileManager(dl, null, null)) {
try (StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null)) {
fm.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(new File(".")));
Iterable<? extends JavaFileObject> files = fm.getJavaFileObjectsFromFiles(Arrays.asList(
new File(testSrc, "test0.java"), new File(testSrc, "test1.java")));
Iterable<String> opts = Arrays.asList("-proc:only",
Iterable<String> opts = Arrays.asList("-XDrawDiagnostics",
"-proc:only",
"-processor", "b6341534",
"-processorpath", testClassPath);
StringWriter out = new StringWriter();
JavacTask task = tool.getTask(out, fm, dl, opts, null, files);
JavacTask task = tool.getTask(out, fm, null, opts, null, files);
task.call();
String s = out.toString();
System.err.print(s);
// Expect the following 2 diagnostics, and no output to log
System.err.println(dl.count + " diagnostics; " + s.length() + " characters");
if (dl.count != 2 || s.length() != 0)
throw new AssertionError("unexpected output from compiler");
s = s.replace(System.getProperty("line.separator"), "\n");
String expected = "test0.java:1:8: compiler.err.duplicate.class: test0\n" +
"1 error\n";
if (!expected.equals(s))
throw new AssertionError("unexpected text in output");
}
}
static class MyDiagListener implements DiagnosticListener<JavaFileObject> {
public void report(Diagnostic d) {
System.err.println(d);
count++;
}
public int count;
}
}

@ -43,7 +43,7 @@ public class b6341534 extends JavacTestingAbstractProcessor {
try {
PackageElement PE = eltUtils.getPackageElement("dir1");
List<? extends Element> LEE = PE.getEnclosedElements(); /* <=This line elicits the error message. */
List<? extends Element> LEE = PE.getEnclosedElements(); //should not crash here
for(Element e : LEE)
System.out.println("found " + e.toString() + " in dir1.");
}

@ -0,0 +1,642 @@
/*
* Copyright (c) 2018, 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 8187950
* @summary Handing of BadClassFile exceptions and CompletionFailures
* @library /tools/javac/lib /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* @build JavacTestingAbstractProcessor MissingClassFile
* @run main MissingClassFile
*/
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ErrorType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import toolbox.*;
import toolbox.Task.*;
import com.sun.source.tree.Scope;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskListener;
import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;
@SupportedAnnotationTypes("*")
public class MissingClassFile {
ToolBox tb = new ToolBox();
void testPackageContent() throws Exception {
Path base = Paths.get(".");
Path libClasses = compileLib(base,
"package pkg;" +
"public class A {" +
"}",
"package pkg;" +
"public class B {" +
"}");
Files.delete(libClasses.resolve("pkg/B.class"));
try (OutputStream out = Files.newOutputStream(libClasses.resolve("pkg/B.class"))) {
out.write(0);
}
doRunTest(base,
t -> {
PackageElement pe = t.getElements().getPackageElement("pkg");
for (Element el : pe.getEnclosedElements()) {
verifyElement(t, el);
}
},
"",
"pkg.B b;");
}
void testPackageDirectAPI() throws Exception {
Path base = Paths.get(".");
Path libClasses = compileLib(base,
"package pkg;" +
"public class A {" +
"}",
"package pkg;" +
"public class B {" +
"}");
Files.delete(libClasses.resolve("pkg/B.class"));
try (OutputStream out = Files.newOutputStream(libClasses.resolve("pkg/B.class"))) {
out.write(0);
}
Path testSrc = base.resolve("test-src");
tb.createDirectories(testSrc);
tb.writeJavaFiles(testSrc,
"package test;\n" +
"public class Test {\n" +
" void t() {\n" +
" pkg.B b;\n" +
" }\n" +
"}\n");
Path testClasses = base.resolve("test-classes");
tb.createDirectories(testClasses);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
List<String> errors = new ArrayList<>();
try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
com.sun.source.util.JavacTask task = (com.sun.source.util.JavacTask)
compiler.getTask(null,
null,
d -> errors.add(d.getCode()),
Arrays.asList("-XDrawDiagnostics",
"-classpath",
libClasses.toString()),
null,
fm.getJavaFileObjects(tb.findJavaFiles(testSrc)));
task.parse();
PackageElement pe = task.getElements().getPackageElement("pkg");
for (Element el : pe.getEnclosedElements()) {
verifyElement(task, el);
}
task.analyze();
}
List<String> expected = Arrays.asList("compiler.err.cant.access");
if (!expected.equals(errors)) {
throw new IllegalStateException("Expected error not found!");
}
}
void testSuperClass() throws Exception {
doTestCombo("class Test {" +
"}",
"package pkg;" +
"public class A extends # {" +
"}",
"pkg.A x;",
"# a = null; a.toString();",
(fqn, t) -> {
TypeElement a = t.getElements()
.getTypeElement(t.getElements()
.getModuleElement(""),
"pkg.A");
TypeMirror superclass = a.getSuperclass();
verifyTypeMirror(t, superclass);
assertEquals(TypeKind.ERROR, superclass.getKind());
Element superclassEl = ((DeclaredType) superclass).asElement();
assertEquals(ElementKind.CLASS, superclassEl.getKind());
assertEquals(TypeKind.ERROR, superclassEl.asType().getKind());
TypeMirror originalType = Trees.instance(t).getOriginalType((ErrorType) superclass);
assertEquals(TypeKind.DECLARED, originalType.getKind());
assertEquals(superclassEl, ((DeclaredType) originalType).asElement());
});
doTestCombo("interface Test {" +
"}",
"package pkg;" +
"public class A implements # {" +
"}",
"pkg.A x;",
"# a = null; a.toString();",
(fqn, t) -> {
TypeElement a = t.getElements().getTypeElement("pkg.A");
TypeMirror superintf = a.getInterfaces().get(0);
verifyTypeMirror(t, superintf);
assertEquals(TypeKind.ERROR, superintf.getKind());
Element superintfEl = ((DeclaredType) superintf).asElement();
//superintfEl.getKind() may be either CLASS or INTERFACE, depending on which class is missing
assertEquals(TypeKind.ERROR, superintfEl.asType().getKind());
TypeMirror originalType = Trees.instance(t).getOriginalType((ErrorType) superintf);
assertEquals(TypeKind.DECLARED, originalType.getKind());
assertEquals(superintfEl, ((DeclaredType) originalType).asElement());
});
doTestCombo("class Test {" +
"}",
"package pkg;" +
"public class A extends # {" +
"}",
"pkg.A x;",
"# a = null; a.toString();",
(fqn, t) -> {
TypeElement a = t.getElements()
.getTypeElement(t.getElements()
.getModuleElement(""),
"pkg.A");
DeclaredType superclass = (DeclaredType) a.getSuperclass();
superclass.getTypeArguments();
});
doTestCombo("class Test {" +
"}",
"package pkg;" +
"public class A extends # {" +
"}",
"pkg.A x;",
"# a = null; a.toString();",
(fqn, t) -> {
TypeElement a = t.getElements()
.getTypeElement(t.getElements()
.getModuleElement(""),
"pkg.A");
DeclaredType superclass = (DeclaredType) a.getSuperclass();
superclass.getEnclosingType();
});
}
void testAnnotation() throws Exception {
doTestCombo("@interface Test {" +
"}",
"package pkg;" +
"@#\n" +
"public class A {" +
"}",
"",
"# a = null; a.toString();",
(fqn, t) -> {
TypeElement a = t.getElements().getTypeElement("pkg.A");
for (AnnotationMirror am : a.getAnnotationMirrors()) {
verifyTypeMirror(t, am.getAnnotationType());
}
});
doTestCombo("@interface Test {" +
" public Class<?> value();" +
"}",
"package pkg;" +
"@#(Object.class)\n" +
"public class A {" +
"}",
"",
"# a = null; a.toString();",
(fqn, t) -> {
TypeElement a = t.getElements().getTypeElement("pkg.A");
for (AnnotationMirror am : a.getAnnotationMirrors()) {
verifyTypeMirror(t, am.getAnnotationType());
if (am.getAnnotationType().toString().equals(fqn)) {
verifyTypeMirror(t, (TypeMirror) am.getElementValues().values()
.iterator().next().getValue());
}
}
});
doTestCombo("class Test { }",
"package pkg;" +
"@Ann(#.class)\n" +
"public class A {" +
"}" +
"@interface Ann {" +
" public Class<?> value();" +
"}",
"",
"# a = null; a.toString();",
(fqn, t) -> {
TypeElement a = t.getElements().getTypeElement("pkg.A");
for (AnnotationMirror am : a.getAnnotationMirrors()) {
verifyTypeMirror(t, am.getAnnotationType());
if (am.getAnnotationType().toString().equals(fqn)) {
verifyTypeMirror(t, (TypeMirror) am.getElementValues().values()
.iterator().next().getValue());
}
}
});
}
void testMethod() throws Exception {
doTestCombo("class Test {" +
"}",
"package pkg;" +
"public class A {" +
" public void m1(# t) { }" +
" public # m2() { return null; }" +
"}",
"",
"pkg.A a = null; a.m2().toString();",
(fqn, t) -> {
TypeElement a = t.getElements().getTypeElement("pkg.A");
List<? extends Element> members = a.getEnclosedElements();
if (members.size() != 3)
throw new AssertionError("Unexpected number of members, " +
"received members: " + members);
for (Element e : members) {
verifyElement(t, e);
}
});
}
void testAnnotationProcessing() throws Exception {
boolean[] superClass = new boolean[1];
boolean[] inInit = new boolean[1];
class TestAP extends AbstractProcessor {
@Override
public void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
if (inInit[0])
doCheck();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (!inInit[0])
doCheck();
return false;
}
private void doCheck() {
com.sun.source.util.JavacTask t = com.sun.source.util.JavacTask.instance(processingEnv);
TypeElement a = t.getElements().getTypeElement("pkg.A");
if (superClass[0]) {
verifyTypeMirror(t, a.getSuperclass());
} else {
verifyTypeMirror(t, a.getInterfaces().get(0));
}
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return Set.of("*");
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
}
for (boolean supClass : new boolean[] {false, true}) {
for (boolean init : new boolean[] {false, true}) {
String decl = supClass ? "class Test { }" : "interface Test { }";
String snip = supClass ? "extends #" : "implements #";
superClass[0] = supClass;
inInit[0] = init;
doTestComboCallBack(decl,
"package pkg;" +
"public class A " + snip + " {" +
"}",
"",
"# a = null; a.toString();",
(fqn, t) -> t.setProcessors(List.of(new TestAP())));
}
}
}
void testGetTypeElement() throws Exception {
doTestCombo("class Test { }",
"package pkg;" +
"public class A extends # {" +
"}",
"",
"pkg.A a = null; a.toString();", //should be generalized/in variant?
(fqn, t) -> {
TypeElement a = t.getElements().getTypeElement(fqn);
if (a != null) {
throw new IllegalStateException();
}
});
}
void testScope() throws Exception {
class Variant {
private final String code;
private final String fqn;
public Variant(String code, String fqn) {
this.code = code;
this.fqn = fqn;
}
}
Path base = Paths.get(".");
Path libClasses = compileLib(base,
"package pkg;" +
"public class A {" +
" public static class I {}" +
"}",
"package pkg;" +
"public class B {" +
"}");
try (OutputStream out = Files.newOutputStream(libClasses.resolve("pkg/B.class"))) {
out.write(0);
}
try (OutputStream out = Files.newOutputStream(libClasses.resolve("pkg/A$I.class"))) {
out.write(0);
}
Path testSrc = base.resolve("test-src");
tb.createDirectories(testSrc);
Path testClasses = base.resolve("test-classes");
tb.createDirectories(testClasses);
Variant[] variants = new Variant[] {
//JDK-8198378:
// new Variant("package test;\n" +
// "import pkg.B;\n" +
// "public class Test {}\n",
// "test.Test"),
new Variant("package test;\n" +
"import pkg.*;\n" +
"public class Test {}\n",
"test.Test"),
new Variant("package test;\n" +
"import pkg.A.*;\n" +
"public class Test extends I {}\n",
"test.Test"),
new Variant("package test;\n" +
"import static pkg.A.*;\n" +
"public class Test extends I {}\n",
"test.Test"),
new Variant("package pkg;\n" +
"public class Test {}\n",
"pkg.Test")
};
for (Variant variant : variants) {
System.err.println("variant: " + variant.code);
tb.writeJavaFiles(testSrc, variant.code);
tb.cleanDirectory(testClasses);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
List<String> errors = new ArrayList<>();
try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
com.sun.source.util.JavacTask task = (com.sun.source.util.JavacTask)
compiler.getTask(null,
null,
d -> errors.add(d.getCode()),
Arrays.asList("-XDrawDiagnostics",
"-classpath",
libClasses.toString()),
null,
fm.getJavaFileObjects(tb.findJavaFiles(testSrc)));
task.analyze();
TypeElement a = task.getElements()
.getTypeElement(task.getElements()
.getModuleElement(""),
variant.fqn);
Trees trees = Trees.instance(task);
TreePath tpA = trees.getPath(a);
Scope scope = trees.getScope(tpA);
while (scope != null) {
for (Element el : scope.getLocalElements()) {
verifyElement(task, el);
}
scope = scope.getEnclosingScope();
}
}
}
}
private Path compileLib(Path base, String... sources) throws Exception {
Path libSrc = base.resolve("lib-src");
tb.createDirectories(libSrc);
tb.writeJavaFiles(libSrc, sources);
Path libClasses = base.resolve("lib-classes");
tb.createDirectories(libClasses);
new JavacTask(tb).outdir(libClasses.toString())
.sourcepath(libSrc.toString())
.files(tb.findJavaFiles(libSrc))
.run()
.writeAll();
return libClasses;
}
private void doTestCombo(String decl,
String use,
String snippetInClass,
String snippetInMethod,
BiConsumer<String, com.sun.source.util.JavacTask> test) throws Exception {
doTestComboCallBack(decl,
use,
snippetInClass,
snippetInMethod,
(fqn, t) -> {
t.addTaskListener(new TaskListener() {
@Override
public void finished(TaskEvent e) {
if (e.getKind() == TaskEvent.Kind.ENTER) {
test.accept(fqn, t);
}
}
});
});
}
private void doTestComboCallBack(String decl,
String use,
String snippetInClass,
String snippetInMethod,
BiConsumer<String, com.sun.source.util.JavacTask> callback) throws Exception {
List<TestVariant> variants = List.of(
new TestVariant("package pkg; public #", "pkg.Test", "pkg/Test.class"),
new TestVariant("package pkg; public class O { public static # }", "pkg.O.Test", "pkg/O$Test.class"),
new TestVariant("package pkg; public class O { public static # }", "pkg.O.Test", "pkg/O.class"),
new TestVariant("package pkg; public class O { public static class N { public static # } }", "pkg.O.N.Test", "pkg/O$N$Test.class"),
new TestVariant("package pkg; public class O { public static class N { public static # } }", "pkg.O.N.Test", "pkg/O$N.class"),
new TestVariant("package pkg; public class O { public static class N { public static # } }", "pkg.O.N.Test", "pkg/O.class")
);
Path base = Paths.get(".");
for (TestVariant v : variants) {
System.err.println("-----------------------------------------------------------------------");
System.err.println("variant: " + v.declarationStub + ", " + v.fqn + ", " + v.path);
Path libClasses = compileLib(base,
use.replace("#", v.fqn),
v.declarationStub.replace("#", decl));
Files.delete(libClasses.resolve(v.path));
doRunTestFullCallback(base,
t -> callback.accept(v.fqn, t),
snippetInClass.replace("#", v.fqn),
snippetInMethod.replace("#", v.fqn));
}
}
private void doRunTest(Path base,
Consumer<com.sun.source.util.JavacTask> test,
String snippetInClass,
String snippetInMethod) throws Exception {
doRunTestFullCallback(base, t -> {
t.addTaskListener(new TaskListener() {
@Override
public void finished(TaskEvent e) {
if (e.getKind() == TaskEvent.Kind.ENTER) {
test.accept(t);
}
}
});
}, snippetInClass, snippetInMethod);
}
private void doRunTestFullCallback(Path base,
Consumer<com.sun.source.util.JavacTask> callback,
String snippetInClass,
String snippetInMethod) throws Exception {
Path libClasses = base.resolve("lib-classes");
Path testSrc = base.resolve("test-src");
tb.createDirectories(testSrc);
tb.writeJavaFiles(testSrc,
"package test;\n" +
"public class Test {\n" +
snippetInClass + "\n" +
" void t() {\n" +
snippetInMethod + "\n" +
" }\n" +
"}\n");
System.err.println("content: " + "package test;\n" +
"public class Test {\n" +
snippetInClass + "\n" +
" void t() {\n" +
snippetInMethod + "\n" +
" }\n" +
"}\n");
Path testClasses = base.resolve("test-classes");
tb.createDirectories(testClasses);
var expectedErrors = new JavacTask(tb).outdir(testClasses.toString())
.options("-XDrawDiagnostics",
"-classpath",
libClasses.toString())
.sourcepath(testSrc.toString())
.files(tb.findJavaFiles(testSrc))
.run(Expect.FAIL)
.writeAll()
.getOutputLines(OutputKind.DIRECT,
OutputKind.STDERR,
OutputKind.STDOUT);
var errors = new JavacTask(tb).outdir(testClasses.toString())
.options("-XDrawDiagnostics",
"-classpath",
libClasses.toString())
.sourcepath(testSrc.toString())
.files(tb.findJavaFiles(testSrc))
.callback(callback)
.run(Expect.FAIL)
.writeAll()
.getOutputLines(OutputKind.DIRECT,
OutputKind.STDERR,
OutputKind.STDOUT);
if (!expectedErrors.equals(errors)) {
throw new IllegalStateException("Expected error not found!");
}
}
private void verifyTypeMirror(com.sun.source.util.JavacTask t, TypeMirror type) {
Element el = t.getTypes().asElement(type);
if (el != null) {
verifyElement(t, el);
}
}
private void verifyElement(com.sun.source.util.JavacTask t, Element el) {
el.getKind(); //forces completion
}
private static void assertEquals(Object expected, Object actual) {
if (!Objects.equals(expected, actual)) {
throw new AssertionError("Unexpected value, expected: " + expected + ", actual: " + actual);
}
}
public static void main(String... args) throws Exception {
MissingClassFile t = new MissingClassFile();
t.testPackageContent();
t.testPackageDirectAPI();
t.testSuperClass();
t.testAnnotation();
t.testAnnotationProcessing();
t.testGetTypeElement();
t.testScope();
}
static class TestVariant {
public final String declarationStub;
public final String fqn;
public final String path;
public TestVariant(String declarationStub, String fqn, String path) {
this.declarationStub = declarationStub;
this.fqn = fqn;
this.path = path;
}
}
}

@ -50,6 +50,7 @@ import java.util.stream.Stream;
import com.sun.tools.javac.api.JavacTaskImpl;
import com.sun.tools.javac.api.JavacTool;
import com.sun.tools.javac.code.DeferredCompletionFailureHandler;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.CompletionFailure;
@ -190,6 +191,10 @@ public class NoAbortForBadClassFile extends TestRunner {
Symtab syms = Symtab.instance(context);
Names names = Names.instance(context);
DeferredCompletionFailureHandler dcfh = DeferredCompletionFailureHandler.instance(context);
dcfh.setHandler(dcfh.javacCodeHandler);
task.getElements().getTypeElement("java.lang.Object");
if (!badClassFile) {

@ -34,6 +34,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.tools.JavaCompiler;
@ -58,6 +59,7 @@ public class JavacTask extends AbstractTask<JavacTask> {
private List<String> files;
private List<JavaFileObject> fileObjects;
private JavaFileManager fileManager;
private Consumer<com.sun.source.util.JavacTask> callback;
private JavaCompiler compiler;
private StandardJavaFileManager internalFileManager;
@ -253,6 +255,16 @@ public class JavacTask extends AbstractTask<JavacTask> {
return this;
}
/**
* Set a callback to be used by this task.
* @param callback the callback
* @return this task object
*/
public JavacTask callback(Consumer<com.sun.source.util.JavacTask> callback) {
this.callback = callback;
return this;
}
/**
* {@inheritDoc}
* @return the name "javac"
@ -290,6 +302,9 @@ public class JavacTask extends AbstractTask<JavacTask> {
if (fileManager != null) {
throw new IllegalStateException("file manager set in CMDLINE mode");
}
if (callback != null) {
throw new IllegalStateException("callback set in CMDLINE mode");
}
rc = runCommand(direct.pw);
break;
default:
@ -333,7 +348,11 @@ public class JavacTask extends AbstractTask<JavacTask> {
allOpts,
classes,
allFiles);
return ((JavacTaskImpl) task).doCall().exitCode;
JavacTaskImpl taskImpl = (JavacTaskImpl) task;
if (callback != null) {
callback.accept(taskImpl);
}
return taskImpl.doCall().exitCode;
} finally {
if (internalFileManager != null)
internalFileManager.close();