# HG changeset patch # User jlahoda # Date 1518524016 -3600 # Tue Feb 13 13:13:36 2018 +0100 # Node ID 007ed8bee26235412908217f90fd26d927f17c05 # Parent 5e2d2067da488466c64af4f4809dae6c4ef44d1f [mq]: 8187950 diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java @@ -38,6 +38,7 @@ 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 @@ private final Arguments args; private JavaCompiler compiler; private JavaFileManager fileManager; + private DeferredCompletionFailureHandler dcfh; private Locale locale; private Map notYetEntered; private ListBuffer> genList; @@ -83,6 +85,8 @@ 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 @@ } private T handleExceptions(Callable c, T sysErrorResult, T abnormalErrorResult) { + Handler prevDeferredHandler = dcfh.setHandler(dcfh.javacCodeHandler); try { return c.call(); } catch (FatalError ex) { @@ -171,6 +176,8 @@ ex.printStackTrace(log.getWriter(WriterKind.NOTICE)); } return abnormalErrorResult; + } finally { + dcfh.setHandler(prevDeferredHandler); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/MultiTaskListener.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/MultiTaskListener.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/MultiTaskListener.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/MultiTaskListener.java @@ -30,6 +30,8 @@ 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 @@ /** 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 @@ protected MultiTaskListener(Context context) { context.put(taskListenerKey, this); ccw = ClientCodeWrapper.instance(context); + dcfh = DeferredCompletionFailureHandler.instance(context); } /** @@ -106,18 +111,28 @@ @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 diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ClassFinder.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ClassFinder.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ClassFinder.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ClassFinder.java @@ -63,6 +63,8 @@ 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 @@ */ 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 @@ 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 @@ 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 @@ } 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 @@ 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 @@ // 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 @@ 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 @@ 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 @@ 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); } } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/DeferredCompletionFailureHandler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/DeferredCompletionFailureHandler.java new file mode 100644 --- /dev/null +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/DeferredCompletionFailureHandler.java @@ -0,0 +1,172 @@ +/* + * 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.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 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() { + 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))); + } + 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; + + private final Map class2Flip = new WeakHashMap<>(); + + 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 { + class2Flip.remove(sym); + 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; + } + + } +} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ModuleFinder.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ModuleFinder.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ModuleFinder.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ModuleFinder.java @@ -89,6 +89,8 @@ private final JCDiagnostic.Factory diags; + private final DeferredCompletionFailureHandler dcfh; + private ModuleNameReader moduleNameReader; public ModuleNameFromSourceReader moduleNameFromSourceReader; @@ -111,6 +113,7 @@ classFinder = ClassFinder.instance(context); diags = JCDiagnostic.Factory.instance(context); + dcfh = DeferredCompletionFailureHandler.instance(context); } class ModuleLocationIterator implements Iterator> { @@ -227,7 +230,7 @@ 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: diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java @@ -47,11 +47,8 @@ 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.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; @@ -634,6 +632,14 @@ } } + 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() { @@ -657,6 +663,7 @@ @DefinedBy(Api.LANGUAGE_MODEL) public Set getModifiers() { + apiComplete(); return Flags.asModifierSet(flags()); } @@ -671,6 +678,7 @@ */ @Override @DefinedBy(Api.LANGUAGE_MODEL) public List getAnnotationMirrors() { + apiComplete(); return getRawAttributes(); } @@ -795,13 +803,11 @@ 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; @@ -980,7 +986,7 @@ @Override @DefinedBy(Api.LANGUAGE_MODEL) public java.util.List getDirectives() { - complete(); + apiComplete(); completeUsesProvides(); return Collections.unmodifiableList(directives); } @@ -1304,9 +1310,11 @@ /** 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); @@ -1316,7 +1324,7 @@ @DefinedBy(Api.LANGUAGE_MODEL) public List getInterfaces() { - complete(); + apiComplete(); if (type instanceof ClassType) { ClassType t = (ClassType)type; if (t.interfaces_field == null) // FIXME: shouldn't be null @@ -1331,7 +1339,7 @@ @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 @@ -1372,6 +1380,7 @@ @DefinedBy(Api.LANGUAGE_MODEL) public ElementKind getKind() { + apiComplete(); long flags = flags(); if ((flags & ANNOTATION) != 0) return ElementKind.ANNOTATION_TYPE; @@ -1385,13 +1394,14 @@ @Override @DefinedBy(Api.LANGUAGE_MODEL) public Set 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()) @@ -2105,13 +2115,15 @@ 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 diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java @@ -4178,9 +4178,6 @@ Name name, List argtypes, List 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); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -146,6 +146,8 @@ */ JCDiagnostic.Factory diagFactory; + DeferredCompletionFailureHandler dcfh; + /** The current scope where type variables are entered. */ protected WriteableScope typevars; @@ -260,6 +262,7 @@ 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 @@ currentOwner.enclClass(), currentClassFile, diagFactory.fragment(key, args), - diagFactory); + diagFactory, + dcfh); } public ClassFinder.BadEnclosingMethodAttr badEnclosingMethod(Symbol sym) { @@ -307,7 +311,8 @@ currentOwner.enclClass(), currentClassFile, diagFactory.fragment(Fragments.BadEnclosingMethod(sym)), - diagFactory); + diagFactory, + dcfh); } /************************************************************************ @@ -2663,7 +2668,7 @@ 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 @@ 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); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java @@ -311,6 +311,8 @@ */ protected JCDiagnostic.Factory diags; + protected DeferredCompletionFailureHandler dcfh; + /** The type eraser. */ protected TransTypes transTypes; @@ -416,6 +418,7 @@ 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 @@ 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 @@ // 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 @@ 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); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java @@ -255,7 +255,8 @@ name.equals(sym.getQualifiedName())) ? clazz.cast(sym) : null; - } catch (CompletionFailure e) { + } catch (CompletionFailure cf) { + cf.dcfh.handleAPICompletionFailure(cf); return null; } } @@ -442,7 +443,7 @@ @DefinedBy(Api.LANGUAGE_MODEL) public boolean isDeprecated(Element e) { Symbol sym = cast(Symbol.class, e); - sym.complete(); + sym.apiComplete(); return sym.isDeprecated(); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java @@ -52,6 +52,7 @@ 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 @@ 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 @@ 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 @@ private ArrayList supportedAnnotationPatterns; private ArrayList 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 @@ throw e; } catch (Throwable t) { throw new AnnotationProcessingError(t); + } finally { + dcfh.setHandler(prevDeferredHandler); } } @@ -767,7 +774,8 @@ 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 @@ private boolean callProcessor(Processor proc, Set tes, RoundEnvironment renv) { + Handler prevDeferredHandler = dcfh.setHandler(dcfh.userCodeHandler); try { return proc.process(tes, renv); } catch (ClassFinder.BadClassFile ex) { @@ -973,6 +982,8 @@ throw e; } catch (Throwable t) { throw new AnnotationProcessingError(t); + } finally { + dcfh.setHandler(prevDeferredHandler); } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java @@ -976,8 +976,7 @@ */ private void addAllClasses(Collection 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); } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/JavadocTool.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/JavadocTool.java --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/JavadocTool.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/JavadocTool.java @@ -38,6 +38,7 @@ 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 @@ final Messager messager; final ClassFinder javadocFinder; + final DeferredCompletionFailureHandler dcfh; final Enter javadocEnter; final Set uniquefiles; @@ -81,6 +83,7 @@ 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 @@ etable.setClassDeclList(listClasses(classTrees.toList())); + dcfh.setHandler(dcfh.userCodeHandler); etable.analyze(); } catch (CompletionFailure cf) { throw new ToolException(ABNORMAL, cf.getMessage(), cf); diff --git a/test/langtools/tools/javac/classreader/T7031108.java b/test/langtools/tools/javac/classreader/T7031108.java --- a/test/langtools/tools/javac/classreader/T7031108.java +++ b/test/langtools/tools/javac/classreader/T7031108.java @@ -63,6 +63,8 @@ + " }\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 @@ 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 @@ List 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; diff --git a/test/langtools/tools/javac/processing/6430209/T6430209.java b/test/langtools/tools/javac/processing/6430209/T6430209.java --- a/test/langtools/tools/javac/processing/6430209/T6430209.java +++ b/test/langtools/tools/javac/processing/6430209/T6430209.java @@ -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 @@ 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 files = fm.getJavaFileObjectsFromFiles(Arrays.asList( new File(testSrc, "test0.java"), new File(testSrc, "test1.java"))); - Iterable opts = Arrays.asList("-proc:only", + Iterable 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 { - public void report(Diagnostic d) { - System.err.println(d); - count++; - } - - public int count; - } } diff --git a/test/langtools/tools/javac/processing/6430209/b6341534.java b/test/langtools/tools/javac/processing/6430209/b6341534.java --- a/test/langtools/tools/javac/processing/6430209/b6341534.java +++ b/test/langtools/tools/javac/processing/6430209/b6341534.java @@ -43,7 +43,7 @@ try { PackageElement PE = eltUtils.getPackageElement("dir1"); - List LEE = PE.getEnclosedElements(); /* <=This line elicits the error message. */ + List LEE = PE.getEnclosedElements(); //should not crash here for(Element e : LEE) System.out.println("found " + e.toString() + " in dir1."); } diff --git a/test/langtools/tools/javac/processing/model/completionfailure/MissingClassFile.java b/test/langtools/tools/javac/processing/model/completionfailure/MissingClassFile.java new file mode 100644 --- /dev/null +++ b/test/langtools/tools/javac/processing/model/completionfailure/MissingClassFile.java @@ -0,0 +1,544 @@ +/* + * 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.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.util.TaskEvent; +import com.sun.source.util.TaskListener; + +@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 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 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.DECLARED, superclass.getKind()); + Element superclassEl = ((DeclaredType) superclass).asElement(); + assertEquals(ElementKind.CLASS, superclassEl.getKind()); + assertEquals(TypeKind.ERROR, superclassEl.asType().getKind()); + }); + 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.DECLARED, 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()); + }); + 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 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 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 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(); + } + }); + } + + 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 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 callback) throws Exception { + List 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 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 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(); + } + + 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; + } + + } +} diff --git a/test/langtools/tools/javac/processing/model/completionfailure/NoAbortForBadClassFile.java b/test/langtools/tools/javac/processing/model/completionfailure/NoAbortForBadClassFile.java --- a/test/langtools/tools/javac/processing/model/completionfailure/NoAbortForBadClassFile.java +++ b/test/langtools/tools/javac/processing/model/completionfailure/NoAbortForBadClassFile.java @@ -50,6 +50,7 @@ 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 @@ 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) { diff --git a/test/langtools/tools/lib/toolbox/JavacTask.java b/test/langtools/tools/lib/toolbox/JavacTask.java --- a/test/langtools/tools/lib/toolbox/JavacTask.java +++ b/test/langtools/tools/lib/toolbox/JavacTask.java @@ -34,6 +34,7 @@ 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 @@ private List files; private List fileObjects; private JavaFileManager fileManager; + private Consumer callback; private JavaCompiler compiler; private StandardJavaFileManager internalFileManager; @@ -254,6 +256,16 @@ } /** + * Set a callback to be used by this task. + * @param callback the callback + * @return this task object + */ + public JavacTask callback(Consumer callback) { + this.callback = callback; + return this; + } + + /** * {@inheritDoc} * @return the name "javac" */ @@ -290,6 +302,9 @@ 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 @@ 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();