8186694: JShell: speed-up compilation by reusing compiler instances
Summary: Generalizing ReusableContext and using it in JShell to speed up processing.
Reviewed-by: mcimadamore, rfield
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java Fri Sep 01 14:04:20 2017 +0200
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2015, 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.
+ *
+ * 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.api;
+
+import java.io.PrintStream;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TaskEvent;
+import com.sun.source.util.TaskEvent.Kind;
+import com.sun.source.util.TaskListener;
+import com.sun.source.util.TreeScanner;
+import com.sun.tools.javac.code.Kinds;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.Type.ClassType;
+import com.sun.tools.javac.code.TypeTag;
+import com.sun.tools.javac.code.Types;
+import com.sun.tools.javac.comp.Annotate;
+import com.sun.tools.javac.comp.Check;
+import com.sun.tools.javac.comp.CompileStates;
+import com.sun.tools.javac.comp.Enter;
+import com.sun.tools.javac.comp.Modules;
+import com.sun.tools.javac.main.Arguments;
+import com.sun.tools.javac.main.JavaCompiler;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import com.sun.tools.javac.model.JavacElements;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.DefinedBy;
+import com.sun.tools.javac.util.DefinedBy.Api;
+import com.sun.tools.javac.util.Log;
+
+/**
+ * A pool of reusable JavacTasks. When a task is no valid anymore, it is returned to the pool,
+ * and its Context may be reused for future processing in some cases. The reuse is achieved
+ * by replacing some components (most notably JavaCompiler and Log) with reusable counterparts,
+ * and by cleaning up leftovers from previous compilation.
+ * <p>
+ * For each combination of options, a separate task/context is created and kept, as most option
+ * values are cached inside components themselves.
+ * <p>
+ * When the compilation redefines sensitive classes (e.g. classes in the the java.* packages), the
+ * task/context is not reused.
+ * <p>
+ * When the task is reused, then packages that were already listed won't be listed again.
+ * <p>
+ * Care must be taken to only return tasks that won't be used by the original caller.
+ * <p>
+ * Care must also be taken when custom components are installed, as those are not cleaned when the
+ * task/context is reused, and subsequent getTask may return a task based on a context with these
+ * custom components.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own risk.
+ * This code and its internal interfaces are subject to change or
+ * deletion without notice.</b>
+ */
+public class JavacTaskPool {
+
+ private static final JavacTool systemProvider = JavacTool.create();
+
+ private final int maxPoolSize;
+ private final Map<List<String>, List<ReusableContext>> options2Contexts = new HashMap<>();
+ private int id;
+
+ private int statReused = 0;
+ private int statNew = 0;
+ private int statPolluted = 0;
+ private int statRemoved = 0;
+
+ /**Creates the pool.
+ *
+ * @param maxPoolSize maximum number of tasks/context that will be kept in the pool.
+ */
+ public JavacTaskPool(int maxPoolSize) {
+ this.maxPoolSize = maxPoolSize;
+ }
+
+ /**Creates a new task as if by {@link javax.tools.JavaCompiler#getTask} and runs the provided
+ * worker with it. The task is only valid while the worker is running. The internal structures
+ * may be reused from some previous compilation.
+ *
+ * @param out a Writer for additional output from the compiler;
+ * use {@code System.err} if {@code null}
+ * @param fileManager a file manager; if {@code null} use the
+ * compiler's standard filemanager
+ * @param diagnosticListener a diagnostic listener; if {@code
+ * null} use the compiler's default method for reporting
+ * diagnostics
+ * @param options compiler options, {@code null} means no options
+ * @param classes names of classes to be processed by annotation
+ * processing, {@code null} means no class names
+ * @param compilationUnits the compilation units to compile, {@code
+ * null} means no compilation units
+ * @param worker that should be run with the task
+ * @return an object representing the compilation
+ * @throws RuntimeException if an unrecoverable error
+ * occurred in a user supplied component. The
+ * {@linkplain Throwable#getCause() cause} will be the error in
+ * user code.
+ * @throws IllegalArgumentException if any of the options are invalid,
+ * or if any of the given compilation units are of other kind than
+ * {@linkplain JavaFileObject.Kind#SOURCE source}
+ */
+ public <Z> Z getTask(Writer out,
+ JavaFileManager fileManager,
+ DiagnosticListener<? super JavaFileObject> diagnosticListener,
+ Iterable<String> options,
+ Iterable<String> classes,
+ Iterable<? extends JavaFileObject> compilationUnits,
+ Worker<Z> worker) {
+ List<String> opts =
+ StreamSupport.stream(options.spliterator(), false)
+ .collect(Collectors.toCollection(ArrayList::new));
+
+ ReusableContext ctx;
+
+ synchronized (this) {
+ List<ReusableContext> cached =
+ options2Contexts.getOrDefault(opts, Collections.emptyList());
+
+ if (cached.isEmpty()) {
+ ctx = new ReusableContext(opts);
+ statNew++;
+ } else {
+ ctx = cached.remove(0);
+ statReused++;
+ }
+ }
+
+ ctx.useCount++;
+
+ JavacTaskImpl task =
+ (JavacTaskImpl) systemProvider.getTask(out, fileManager, diagnosticListener,
+ opts, classes, compilationUnits, ctx);
+
+ task.addTaskListener(ctx);
+
+ Z result = worker.withTask(task);
+
+ //not returning the context to the pool if task crashes with an exception
+ //the task/context may be in a broken state
+ ctx.clear();
+ if (ctx.polluted) {
+ statPolluted++;
+ } else {
+ task.cleanup();
+ synchronized (this) {
+ while (cacheSize() + 1 > maxPoolSize) {
+ ReusableContext toRemove =
+ options2Contexts.values()
+ .stream()
+ .flatMap(Collection::stream)
+ .sorted((c1, c2) -> c1.timeStamp < c2.timeStamp ? -1 : 1)
+ .findFirst()
+ .get();
+ options2Contexts.get(toRemove.arguments).remove(toRemove);
+ statRemoved++;
+ }
+ options2Contexts.computeIfAbsent(ctx.arguments, x -> new ArrayList<>()).add(ctx);
+ ctx.timeStamp = id++;
+ }
+ }
+
+ return result;
+ }
+ //where:
+ private long cacheSize() {
+ return options2Contexts.values().stream().flatMap(Collection::stream).count();
+ }
+
+ public void printStatistics(PrintStream out) {
+ out.println(statReused + " reused Contexts");
+ out.println(statNew + " newly created Contexts");
+ out.println(statPolluted + " polluted Contexts");
+ out.println(statRemoved + " removed Contexts");
+ }
+
+ public interface Worker<Z> {
+ public Z withTask(JavacTask task);
+ }
+
+ static class ReusableContext extends Context implements TaskListener {
+
+ Set<CompilationUnitTree> roots = new HashSet<>();
+
+ List<String> arguments;
+ boolean polluted = false;
+
+ int useCount;
+ long timeStamp;
+
+ ReusableContext(List<String> arguments) {
+ super();
+ this.arguments = arguments;
+ put(Log.logKey, ReusableLog.factory);
+ put(JavaCompiler.compilerKey, ReusableJavaCompiler.factory);
+ }
+
+ void clear() {
+ drop(Arguments.argsKey);
+ drop(DiagnosticListener.class);
+ drop(Log.outKey);
+ drop(Log.errKey);
+ drop(JavaFileManager.class);
+ drop(JavacTask.class);
+ drop(JavacTrees.class);
+ drop(JavacElements.class);
+
+ if (ht.get(Log.logKey) instanceof ReusableLog) {
+ //log already inited - not first round
+ ((ReusableLog)Log.instance(this)).clear();
+ Enter.instance(this).newRound();
+ ((ReusableJavaCompiler)ReusableJavaCompiler.instance(this)).clear();
+ Types.instance(this).newRound();
+ Check.instance(this).newRound();
+ Modules.instance(this).newRound();
+ Annotate.instance(this).newRound();
+ CompileStates.instance(this).clear();
+ MultiTaskListener.instance(this).clear();
+
+ //find if any of the roots have redefined java.* classes
+ Symtab syms = Symtab.instance(this);
+ pollutionScanner.scan(roots, syms);
+ roots.clear();
+ }
+ }
+
+ /**
+ * This scanner detects as to whether the shared context has been polluted. This happens
+ * whenever a compiled program redefines a core class (in 'java.*' package) or when
+ * (typically because of cyclic inheritance) the symbol kind of a core class has been touched.
+ */
+ TreeScanner<Void, Symtab> pollutionScanner = new TreeScanner<Void, Symtab>() {
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Void visitClass(ClassTree node, Symtab syms) {
+ Symbol sym = ((JCClassDecl)node).sym;
+ if (sym != null) {
+ syms.removeClass(sym.packge().modle, sym.flatName());
+ Type sup = supertype(sym);
+ if (isCoreClass(sym) ||
+ (sup != null && isCoreClass(sup.tsym) && sup.tsym.kind != Kinds.Kind.TYP)) {
+ polluted = true;
+ }
+ }
+ return super.visitClass(node, syms);
+ }
+
+ private boolean isCoreClass(Symbol s) {
+ return s.flatName().toString().startsWith("java.");
+ }
+
+ private Type supertype(Symbol s) {
+ if (s.type == null ||
+ !s.type.hasTag(TypeTag.CLASS)) {
+ return null;
+ } else {
+ ClassType ct = (ClassType)s.type;
+ return ct.supertype_field;
+ }
+ }
+ };
+
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public void finished(TaskEvent e) {
+ if (e.getKind() == Kind.PARSE) {
+ roots.add(e.getCompilationUnit());
+ }
+ }
+
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public void started(TaskEvent e) {
+ //do nothing
+ }
+
+ <T> void drop(Key<T> k) {
+ ht.remove(k);
+ }
+
+ <T> void drop(Class<T> c) {
+ ht.remove(key(c));
+ }
+
+ /**
+ * Reusable JavaCompiler; exposes a method to clean up the component from leftovers associated with
+ * previous compilations.
+ */
+ static class ReusableJavaCompiler extends JavaCompiler {
+
+ final static Factory<JavaCompiler> factory = ReusableJavaCompiler::new;
+
+ ReusableJavaCompiler(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void close() {
+ //do nothing
+ }
+
+ void clear() {
+ newRound();
+ }
+
+ @Override
+ protected void checkReusable() {
+ //do nothing - it's ok to reuse the compiler
+ }
+ }
+
+ /**
+ * Reusable Log; exposes a method to clean up the component from leftovers associated with
+ * previous compilations.
+ */
+ static class ReusableLog extends Log {
+
+ final static Factory<Log> factory = ReusableLog::new;
+
+ Context context;
+
+ ReusableLog(Context context) {
+ super(context);
+ this.context = context;
+ }
+
+ void clear() {
+ recorded.clear();
+ sourceMap.clear();
+ nerrors = 0;
+ nwarnings = 0;
+ //Set a fake listener that will lazily lookup the context for the 'real' listener. Since
+ //this field is never updated when a new task is created, we cannot simply reset the field
+ //or keep old value. This is a hack to workaround the limitations in the current infrastructure.
+ diagListener = new DiagnosticListener<JavaFileObject>() {
+ DiagnosticListener<JavaFileObject> cachedListener;
+
+ @Override @DefinedBy(Api.COMPILER)
+ @SuppressWarnings("unchecked")
+ public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+ if (cachedListener == null) {
+ cachedListener = context.get(DiagnosticListener.class);
+ }
+ cachedListener.report(diagnostic);
+ }
+ };
+ }
+ }
+ }
+}
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java Mon Oct 16 18:15:41 2017 +0530
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java Fri Sep 01 14:04:20 2017 +0200
@@ -241,12 +241,19 @@
boolean firstToComplete = queue.isEmpty();
Phase prevTopLevelPhase = topLevelPhase;
+ boolean success = false;
try {
topLevelPhase = this;
doCompleteEnvs(envs);
+ success = true;
} finally {
topLevelPhase = prevTopLevelPhase;
+ if (!success && firstToComplete) {
+ //an exception was thrown, e.g. BreakAttr:
+ //the queue would become stale, clear it:
+ queue.clear();
+ }
}
if (firstToComplete) {
--- a/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java Mon Oct 16 18:15:41 2017 +0530
+++ b/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java Fri Sep 01 14:04:20 2017 +0200
@@ -45,6 +45,7 @@
import com.sun.source.tree.Tree;
import static jdk.jshell.CompletenessAnalyzer.TK.*;
import jdk.jshell.TaskFactory.ParseTask;
+import jdk.jshell.TaskFactory.Worker;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -85,7 +86,7 @@
try {
Parser parser = new Parser(
() -> new Matched(scannerFactory.newScanner(s, false)),
- () -> proc.taskFactory.parse(s));
+ worker -> proc.taskFactory.parse(s, worker));
Completeness stat = parser.parseUnit();
int endPos = stat == Completeness.UNKNOWN
? s.length()
@@ -561,12 +562,13 @@
private static class Parser {
private final Supplier<Matched> matchedFactory;
- private final Supplier<ParseTask> parseFactory;
+ private final Function<Worker<ParseTask, Completeness>, Completeness> parseFactory;
private Matched in;
private CT token;
private Completeness checkResult;
- Parser(Supplier<Matched> matchedFactory, Supplier<ParseTask> parseFactory) {
+ Parser(Supplier<Matched> matchedFactory,
+ Function<Worker<ParseTask, Completeness>, Completeness> parseFactory) {
this.matchedFactory = matchedFactory;
this.parseFactory = parseFactory;
resetInput();
@@ -692,30 +694,31 @@
public Completeness disambiguateDeclarationVsExpression() {
// String folding messes up position information.
- ParseTask pt = parseFactory.get();
- List<? extends Tree> units = pt.units();
- if (units.isEmpty()) {
- return error();
- }
- Tree unitTree = units.get(0);
- switch (unitTree.getKind()) {
- case EXPRESSION_STATEMENT:
- return parseExpressionOptionalSemi();
- case LABELED_STATEMENT:
- if (shouldAbort(IDENTIFIER)) return checkResult;
- if (shouldAbort(COLON)) return checkResult;
- return parseStatement();
- case VARIABLE:
- case IMPORT:
- case CLASS:
- case ENUM:
- case ANNOTATION_TYPE:
- case INTERFACE:
- case METHOD:
- return parseDeclaration();
- default:
+ return parseFactory.apply(pt -> {
+ List<? extends Tree> units = pt.units();
+ if (units.isEmpty()) {
return error();
- }
+ }
+ Tree unitTree = units.get(0);
+ switch (unitTree.getKind()) {
+ case EXPRESSION_STATEMENT:
+ return parseExpressionOptionalSemi();
+ case LABELED_STATEMENT:
+ if (shouldAbort(IDENTIFIER)) return checkResult;
+ if (shouldAbort(COLON)) return checkResult;
+ return parseStatement();
+ case VARIABLE:
+ case IMPORT:
+ case CLASS:
+ case ENUM:
+ case ANNOTATION_TYPE:
+ case INTERFACE:
+ case METHOD:
+ return parseDeclaration();
+ default:
+ return error();
+ }
+ });
}
public Completeness parseExpressionStatement() {
--- a/src/jdk.jshell/share/classes/jdk/jshell/Eval.java Mon Oct 16 18:15:41 2017 +0530
+++ b/src/jdk.jshell/share/classes/jdk/jshell/Eval.java Fri Sep 01 14:04:20 2017 +0200
@@ -177,40 +177,41 @@
if (compileSource.length() == 0) {
return Collections.emptyList();
}
- ParseTask pt = state.taskFactory.parse(compileSource);
- List<? extends Tree> units = pt.units();
- if (units.isEmpty()) {
- return compileFailResult(pt, userSource, Kind.ERRONEOUS);
- }
- Tree unitTree = units.get(0);
- if (pt.getDiagnostics().hasOtherThanNotStatementErrors()) {
- return compileFailResult(pt, userSource, kindOfTree(unitTree));
- }
+ return state.taskFactory.parse(compileSource, pt -> {
+ List<? extends Tree> units = pt.units();
+ if (units.isEmpty()) {
+ return compileFailResult(pt, userSource, Kind.ERRONEOUS);
+ }
+ Tree unitTree = units.get(0);
+ if (pt.getDiagnostics().hasOtherThanNotStatementErrors()) {
+ return compileFailResult(pt, userSource, kindOfTree(unitTree));
+ }
- // Erase illegal/ignored modifiers
- compileSource = new MaskCommentsAndModifiers(compileSource, true).cleared();
+ // Erase illegal/ignored modifiers
+ String compileSourceInt = new MaskCommentsAndModifiers(compileSource, true).cleared();
- state.debug(DBG_GEN, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
- switch (unitTree.getKind()) {
- case IMPORT:
- return processImport(userSource, compileSource);
- case VARIABLE:
- return processVariables(userSource, units, compileSource, pt);
- case EXPRESSION_STATEMENT:
- return processExpression(userSource, compileSource);
- case CLASS:
- return processClass(userSource, unitTree, compileSource, SubKind.CLASS_SUBKIND, pt);
- case ENUM:
- return processClass(userSource, unitTree, compileSource, SubKind.ENUM_SUBKIND, pt);
- case ANNOTATION_TYPE:
- return processClass(userSource, unitTree, compileSource, SubKind.ANNOTATION_TYPE_SUBKIND, pt);
- case INTERFACE:
- return processClass(userSource, unitTree, compileSource, SubKind.INTERFACE_SUBKIND, pt);
- case METHOD:
- return processMethod(userSource, unitTree, compileSource, pt);
- default:
- return processStatement(userSource, compileSource);
- }
+ state.debug(DBG_GEN, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
+ switch (unitTree.getKind()) {
+ case IMPORT:
+ return processImport(userSource, compileSourceInt);
+ case VARIABLE:
+ return processVariables(userSource, units, compileSourceInt, pt);
+ case EXPRESSION_STATEMENT:
+ return processExpression(userSource, compileSourceInt);
+ case CLASS:
+ return processClass(userSource, unitTree, compileSourceInt, SubKind.CLASS_SUBKIND, pt);
+ case ENUM:
+ return processClass(userSource, unitTree, compileSourceInt, SubKind.ENUM_SUBKIND, pt);
+ case ANNOTATION_TYPE:
+ return processClass(userSource, unitTree, compileSourceInt, SubKind.ANNOTATION_TYPE_SUBKIND, pt);
+ case INTERFACE:
+ return processClass(userSource, unitTree, compileSourceInt, SubKind.INTERFACE_SUBKIND, pt);
+ case METHOD:
+ return processMethod(userSource, unitTree, compileSourceInt, pt);
+ default:
+ return processStatement(userSource, compileSourceInt);
+ }
+ });
}
private List<Snippet> processImport(String userSource, String compileSource) {
@@ -295,9 +296,9 @@
Range rtype = dis.treeToRange(baseType);
typeWrap = Wrap.rangeWrap(compileSource, rtype);
} else {
- AnalyzeTask at = trialCompile(Wrap.methodWrap(compileSource));
- if (at.hasErrors()) {
- return compileFailResult(at, userSource, kindOfTree(unitTree));
+ DiagList dl = trialCompile(Wrap.methodWrap(compileSource));
+ if (dl.hasErrors()) {
+ return compileFailResult(dl, userSource, kindOfTree(unitTree));
}
Tree init = vt.getInitializer();
if (init != null) {
@@ -459,13 +460,13 @@
guts = Wrap.methodWrap(compileSource);
if (ei == null) {
// We got no type info, check for not a statement by trying
- AnalyzeTask at = trialCompile(guts);
- if (at.getDiagnostics().hasNotStatement()) {
+ DiagList dl = trialCompile(guts);
+ if (dl.hasNotStatement()) {
guts = Wrap.methodReturnWrap(compileSource);
- at = trialCompile(guts);
+ dl = trialCompile(guts);
}
- if (at.hasErrors()) {
- return compileFailResult(at, userSource, Kind.EXPRESSION);
+ if (dl.hasErrors()) {
+ return compileFailResult(dl, userSource, Kind.EXPRESSION);
}
}
snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
@@ -496,32 +497,32 @@
private List<Snippet> processStatement(String userSource, String compileSource) {
Wrap guts = Wrap.methodWrap(compileSource);
// Check for unreachable by trying
- AnalyzeTask at = trialCompile(guts);
- if (at.hasErrors()) {
- if (at.getDiagnostics().hasUnreachableError()) {
+ DiagList dl = trialCompile(guts);
+ if (dl.hasErrors()) {
+ if (dl.hasUnreachableError()) {
guts = Wrap.methodUnreachableSemiWrap(compileSource);
- at = trialCompile(guts);
- if (at.hasErrors()) {
- if (at.getDiagnostics().hasUnreachableError()) {
+ dl = trialCompile(guts);
+ if (dl.hasErrors()) {
+ if (dl.hasUnreachableError()) {
// Without ending semicolon
guts = Wrap.methodUnreachableWrap(compileSource);
- at = trialCompile(guts);
+ dl = trialCompile(guts);
}
- if (at.hasErrors()) {
- return compileFailResult(at, userSource, Kind.STATEMENT);
+ if (dl.hasErrors()) {
+ return compileFailResult(dl, userSource, Kind.STATEMENT);
}
}
} else {
- return compileFailResult(at, userSource, Kind.STATEMENT);
+ return compileFailResult(dl, userSource, Kind.STATEMENT);
}
}
Snippet snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
return singletonList(snip);
}
- private AnalyzeTask trialCompile(Wrap guts) {
+ private DiagList trialCompile(Wrap guts) {
OuterWrap outer = state.outerMap.wrapInTrialClass(guts);
- return state.taskFactory.new AnalyzeTask(outer);
+ return state.taskFactory.analyze(outer, AnalyzeTask::getDiagnostics);
}
private List<Snippet> processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt) {
@@ -751,19 +752,22 @@
ins.stream().forEach(Unit::initialize);
ins.stream().forEach(u -> u.setWrap(ins, ins));
- AnalyzeTask at = state.taskFactory.new AnalyzeTask(outerWrapSet(ins));
- ins.stream().forEach(u -> u.setDiagnostics(at));
+ state.taskFactory.analyze(outerWrapSet(ins), at -> {
+ ins.stream().forEach(u -> u.setDiagnostics(at));
- // corral any Snippets that need it
- AnalyzeTask cat;
- if (ins.stream().anyMatch(u -> u.corralIfNeeded(ins))) {
- // if any were corralled, re-analyze everything
- cat = state.taskFactory.new AnalyzeTask(outerWrapSet(ins));
- ins.stream().forEach(u -> u.setCorralledDiagnostics(cat));
- } else {
- cat = at;
- }
- ins.stream().forEach(u -> u.setStatus(cat));
+ // corral any Snippets that need it
+ if (ins.stream().anyMatch(u -> u.corralIfNeeded(ins))) {
+ // if any were corralled, re-analyze everything
+ state.taskFactory.analyze(outerWrapSet(ins), cat -> {
+ ins.stream().forEach(u -> u.setCorralledDiagnostics(cat));
+ ins.stream().forEach(u -> u.setStatus(cat));
+ return null;
+ });
+ } else {
+ ins.stream().forEach(u -> u.setStatus(at));
+ }
+ return null;
+ });
// compile and load the legit snippets
boolean success;
while (true) {
@@ -780,37 +784,45 @@
legit.stream().forEach(u -> u.setWrap(ins, legit));
// generate class files for those capable
- CompileTask ct = state.taskFactory.new CompileTask(outerWrapSet(legit));
- if (!ct.compile()) {
- // oy! compile failed because of recursive new unresolved
- if (legit.stream()
- .filter(u -> u.smashingErrorDiagnostics(ct))
- .count() > 0) {
- // try again, with the erroreous removed
- continue;
- } else {
- state.debug(DBG_GEN, "Should never happen error-less failure - %s\n",
- legit);
+ Result res = state.taskFactory.compile(outerWrapSet(legit), ct -> {
+ if (!ct.compile()) {
+ // oy! compile failed because of recursive new unresolved
+ if (legit.stream()
+ .filter(u -> u.smashingErrorDiagnostics(ct))
+ .count() > 0) {
+ // try again, with the erroreous removed
+ return Result.CONTINUE;
+ } else {
+ state.debug(DBG_GEN, "Should never happen error-less failure - %s\n",
+ legit);
+ }
}
- }
- // load all new classes
- load(legit.stream()
- .flatMap(u -> u.classesToLoad(ct.classList(u.snippet().outerWrap())))
- .collect(toSet()));
- // attempt to redefine the remaining classes
- List<Unit> toReplace = legit.stream()
- .filter(u -> !u.doRedefines())
- .collect(toList());
+ // load all new classes
+ load(legit.stream()
+ .flatMap(u -> u.classesToLoad(ct.classList(u.snippet().outerWrap())))
+ .collect(toSet()));
+ // attempt to redefine the remaining classes
+ List<Unit> toReplace = legit.stream()
+ .filter(u -> !u.doRedefines())
+ .collect(toList());
- // prevent alternating redefine/replace cyclic dependency
- // loop by replacing all that have been replaced
- if (!toReplace.isEmpty()) {
- replaced.addAll(toReplace);
- replaced.stream().forEach(Unit::markForReplacement);
+ // prevent alternating redefine/replace cyclic dependency
+ // loop by replacing all that have been replaced
+ if (!toReplace.isEmpty()) {
+ replaced.addAll(toReplace);
+ replaced.stream().forEach(Unit::markForReplacement);
+ }
+
+ return toReplace.isEmpty() ? Result.SUCESS : Result.FAILURE;
+ });
+
+ switch (res) {
+ case CONTINUE: continue;
+ case SUCESS: success = true; break;
+ default:
+ case FAILURE: success = false; break;
}
-
- success = toReplace.isEmpty();
}
break;
}
@@ -830,6 +842,8 @@
}
}
}
+ //where:
+ enum Result {SUCESS, FAILURE, CONTINUE}
/**
* If there are classes to load, loads by calling the execution engine.
@@ -893,6 +907,8 @@
final boolean fatal;
final String message;
+ long start;
+ long end;
ModifierDiagnostic(List<Modifier> list, boolean fatal) {
this.fatal = fatal;
@@ -910,6 +926,8 @@
? "jshell.diag.modifier.single.fatal"
: "jshell.diag.modifier.single.ignore";
this.message = state.messageFormat(key, sb.toString());
+ start = dis.getStartPosition(modtree);
+ end = dis.getEndPosition(modtree);
}
@Override
@@ -919,17 +937,17 @@
@Override
public long getPosition() {
- return dis.getStartPosition(modtree);
+ return start;
}
@Override
public long getStartPosition() {
- return dis.getStartPosition(modtree);
+ return start;
}
@Override
public long getEndPosition() {
- return dis.getEndPosition(modtree);
+ return end;
}
@Override
--- a/src/jdk.jshell/share/classes/jdk/jshell/ExpressionToTypeInfo.java Mon Oct 16 18:15:41 2017 +0530
+++ b/src/jdk.jshell/share/classes/jdk/jshell/ExpressionToTypeInfo.java Fri Sep 01 14:04:20 2017 +0200
@@ -163,14 +163,15 @@
if (code == null || code.isEmpty()) {
return null;
}
+ OuterWrap codeWrap = state.outerMap.wrapInTrialClass(Wrap.methodReturnWrap(code));
try {
- OuterWrap codeWrap = state.outerMap.wrapInTrialClass(Wrap.methodReturnWrap(code));
- AnalyzeTask at = state.taskFactory.new AnalyzeTask(codeWrap);
- CompilationUnitTree cu = at.firstCuTree();
- if (at.hasErrors() || cu == null) {
- return null;
- }
- return new ExpressionToTypeInfo(at, cu, state).typeOfExpression();
+ return state.taskFactory.analyze(codeWrap, at -> {
+ CompilationUnitTree cu = at.firstCuTree();
+ if (at.hasErrors() || cu == null) {
+ return null;
+ }
+ return new ExpressionToTypeInfo(at, cu, state).typeOfExpression();
+ });
} catch (Exception ex) {
return null;
}
@@ -189,12 +190,13 @@
}
try {
OuterWrap codeWrap = state.outerMap.wrapInTrialClass(Wrap.methodWrap("var $$$ = " + code));
- AnalyzeTask at = state.taskFactory.new AnalyzeTask(codeWrap);
- CompilationUnitTree cu = at.firstCuTree();
- if (at.hasErrors() || cu == null) {
- return null;
- }
- return new ExpressionToTypeInfo(at, cu, state).typeOfExpression();
+ return state.taskFactory.analyze(codeWrap, at -> {
+ CompilationUnitTree cu = at.firstCuTree();
+ if (at.hasErrors() || cu == null) {
+ return null;
+ }
+ return new ExpressionToTypeInfo(at, cu, state).typeOfExpression();
+ });
} catch (Exception ex) {
return null;
}
--- a/src/jdk.jshell/share/classes/jdk/jshell/ReplParserFactory.java Mon Oct 16 18:15:41 2017 +0530
+++ b/src/jdk.jshell/share/classes/jdk/jshell/ReplParserFactory.java Fri Sep 01 14:04:20 2017 +0200
@@ -40,8 +40,12 @@
private final boolean forceExpression;
public static void preRegister(Context context, boolean forceExpression) {
- context.put(parserFactoryKey, (Context.Factory<ParserFactory>)
- (c -> new ReplParserFactory(c, forceExpression)));
+ class Mark {}
+ if (context.get(Mark.class) == null) { //don't register the factory if Context is reused
+ context.put(parserFactoryKey, (Context.Factory<ParserFactory>)
+ (c -> new ReplParserFactory(c, forceExpression)));
+ context.put(Mark.class, new Mark());
+ }
}
private final ScannerFactory scannerFactory;
--- a/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java Mon Oct 16 18:15:41 2017 +0530
+++ b/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java Fri Sep 01 14:04:20 2017 +0200
@@ -236,14 +236,15 @@
}
private Tree.Kind guessKind(String code) {
- ParseTask pt = proc.taskFactory.parse(code);
- List<? extends Tree> units = pt.units();
- if (units.isEmpty()) {
- return Tree.Kind.BLOCK;
- }
- Tree unitTree = units.get(0);
- proc.debug(DBG_COMPA, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
- return unitTree.getKind();
+ return proc.taskFactory.parse(code, pt -> {
+ List<? extends Tree> units = pt.units();
+ if (units.isEmpty()) {
+ return Tree.Kind.BLOCK;
+ }
+ Tree unitTree = units.get(0);
+ proc.debug(DBG_COMPA, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
+ return unitTree.getKind();
+ });
}
//TODO: would be better handled through a lexer:
@@ -295,182 +296,183 @@
}
private List<Suggestion> computeSuggestions(OuterWrap code, int cursor, int[] anchor) {
- AnalyzeTask at = proc.taskFactory.new AnalyzeTask(code);
- SourcePositions sp = at.trees().getSourcePositions();
- CompilationUnitTree topLevel = at.firstCuTree();
- List<Suggestion> result = new ArrayList<>();
- TreePath tp = pathFor(topLevel, sp, code.snippetIndexToWrapIndex(cursor));
- if (tp != null) {
- Scope scope = at.trees().getScope(tp);
- Predicate<Element> accessibility = createAccessibilityFilter(at, tp);
- Predicate<Element> smartTypeFilter;
- Predicate<Element> smartFilter;
- Iterable<TypeMirror> targetTypes = findTargetType(at, tp);
- if (targetTypes != null) {
- smartTypeFilter = el -> {
- TypeMirror resultOf = resultTypeOf(el);
- return Util.stream(targetTypes)
- .anyMatch(targetType -> at.getTypes().isAssignable(resultOf, targetType));
- };
-
- smartFilter = IS_CLASS.negate()
- .and(IS_INTERFACE.negate())
- .and(IS_PACKAGE.negate())
- .and(smartTypeFilter);
- } else {
- smartFilter = TRUE;
- smartTypeFilter = TRUE;
- }
- switch (tp.getLeaf().getKind()) {
- case MEMBER_SELECT: {
- MemberSelectTree mst = (MemberSelectTree)tp.getLeaf();
- if (mst.getIdentifier().contentEquals("*"))
- break;
- TreePath exprPath = new TreePath(tp, mst.getExpression());
- TypeMirror site = at.trees().getTypeMirror(exprPath);
- boolean staticOnly = isStaticContext(at, exprPath);
- ImportTree it = findImport(tp);
- boolean isImport = it != null;
-
- List<? extends Element> members = membersOf(at, site, staticOnly && !isImport);
- Predicate<Element> filter = accessibility;
- Function<Boolean, String> paren = DEFAULT_PAREN;
+ return proc.taskFactory.analyze(code, at -> {
+ SourcePositions sp = at.trees().getSourcePositions();
+ CompilationUnitTree topLevel = at.firstCuTree();
+ List<Suggestion> result = new ArrayList<>();
+ TreePath tp = pathFor(topLevel, sp, code.snippetIndexToWrapIndex(cursor));
+ if (tp != null) {
+ Scope scope = at.trees().getScope(tp);
+ Predicate<Element> accessibility = createAccessibilityFilter(at, tp);
+ Predicate<Element> smartTypeFilter;
+ Predicate<Element> smartFilter;
+ Iterable<TypeMirror> targetTypes = findTargetType(at, tp);
+ if (targetTypes != null) {
+ smartTypeFilter = el -> {
+ TypeMirror resultOf = resultTypeOf(el);
+ return Util.stream(targetTypes)
+ .anyMatch(targetType -> at.getTypes().isAssignable(resultOf, targetType));
+ };
- if (isNewClass(tp)) { // new xxx.|
- Predicate<Element> constructorFilter = accessibility.and(IS_CONSTRUCTOR)
- .and(el -> {
- if (el.getEnclosingElement().getEnclosingElement().getKind() == ElementKind.CLASS) {
- return el.getEnclosingElement().getModifiers().contains(Modifier.STATIC);
- }
- return true;
- });
- addElements(membersOf(at, members), constructorFilter, smartFilter, result);
+ smartFilter = IS_CLASS.negate()
+ .and(IS_INTERFACE.negate())
+ .and(IS_PACKAGE.negate())
+ .and(smartTypeFilter);
+ } else {
+ smartFilter = TRUE;
+ smartTypeFilter = TRUE;
+ }
+ switch (tp.getLeaf().getKind()) {
+ case MEMBER_SELECT: {
+ MemberSelectTree mst = (MemberSelectTree)tp.getLeaf();
+ if (mst.getIdentifier().contentEquals("*"))
+ break;
+ TreePath exprPath = new TreePath(tp, mst.getExpression());
+ TypeMirror site = at.trees().getTypeMirror(exprPath);
+ boolean staticOnly = isStaticContext(at, exprPath);
+ ImportTree it = findImport(tp);
+ boolean isImport = it != null;
+
+ List<? extends Element> members = membersOf(at, site, staticOnly && !isImport);
+ Predicate<Element> filter = accessibility;
+ Function<Boolean, String> paren = DEFAULT_PAREN;
- filter = filter.and(IS_PACKAGE);
- } else if (isThrowsClause(tp)) {
- staticOnly = true;
- filter = filter.and(IS_PACKAGE.or(IS_CLASS).or(IS_INTERFACE));
- smartFilter = IS_PACKAGE.negate().and(smartTypeFilter);
- } else if (isImport) {
- paren = NO_PAREN;
- if (!it.isStatic()) {
+ if (isNewClass(tp)) { // new xxx.|
+ Predicate<Element> constructorFilter = accessibility.and(IS_CONSTRUCTOR)
+ .and(el -> {
+ if (el.getEnclosingElement().getEnclosingElement().getKind() == ElementKind.CLASS) {
+ return el.getEnclosingElement().getModifiers().contains(Modifier.STATIC);
+ }
+ return true;
+ });
+ addElements(membersOf(at, members), constructorFilter, smartFilter, result);
+
+ filter = filter.and(IS_PACKAGE);
+ } else if (isThrowsClause(tp)) {
+ staticOnly = true;
filter = filter.and(IS_PACKAGE.or(IS_CLASS).or(IS_INTERFACE));
+ smartFilter = IS_PACKAGE.negate().and(smartTypeFilter);
+ } else if (isImport) {
+ paren = NO_PAREN;
+ if (!it.isStatic()) {
+ filter = filter.and(IS_PACKAGE.or(IS_CLASS).or(IS_INTERFACE));
+ }
+ } else {
+ filter = filter.and(IS_CONSTRUCTOR.negate());
}
- } else {
- filter = filter.and(IS_CONSTRUCTOR.negate());
- }
-
- filter = filter.and(staticOnly ? STATIC_ONLY : INSTANCE_ONLY);
- addElements(members, filter, smartFilter, paren, result);
- break;
- }
- case IDENTIFIER:
- if (isNewClass(tp)) {
- Function<Element, Iterable<? extends Element>> listEnclosed =
- el -> el.getKind() == ElementKind.PACKAGE ? Collections.singletonList(el)
- : el.getEnclosedElements();
- Predicate<Element> filter = accessibility.and(IS_CONSTRUCTOR.or(IS_PACKAGE));
- NewClassTree newClassTree = (NewClassTree)tp.getParentPath().getLeaf();
- ExpressionTree enclosingExpression = newClassTree.getEnclosingExpression();
- if (enclosingExpression != null) { // expr.new IDENT|
- TypeMirror site = at.trees().getTypeMirror(new TreePath(tp, enclosingExpression));
- filter = filter.and(el -> el.getEnclosingElement().getKind() == ElementKind.CLASS && !el.getEnclosingElement().getModifiers().contains(Modifier.STATIC));
- addElements(membersOf(at, membersOf(at, site, false)), filter, smartFilter, result);
- } else {
- addScopeElements(at, scope, listEnclosed, filter, smartFilter, result);
- }
+ filter = filter.and(staticOnly ? STATIC_ONLY : INSTANCE_ONLY);
+
+ addElements(members, filter, smartFilter, paren, result);
break;
}
- if (isThrowsClause(tp)) {
- Predicate<Element> accept = accessibility.and(STATIC_ONLY)
- .and(IS_PACKAGE.or(IS_CLASS).or(IS_INTERFACE));
- addScopeElements(at, scope, IDENTITY, accept, IS_PACKAGE.negate().and(smartTypeFilter), result);
+ case IDENTIFIER:
+ if (isNewClass(tp)) {
+ Function<Element, Iterable<? extends Element>> listEnclosed =
+ el -> el.getKind() == ElementKind.PACKAGE ? Collections.singletonList(el)
+ : el.getEnclosedElements();
+ Predicate<Element> filter = accessibility.and(IS_CONSTRUCTOR.or(IS_PACKAGE));
+ NewClassTree newClassTree = (NewClassTree)tp.getParentPath().getLeaf();
+ ExpressionTree enclosingExpression = newClassTree.getEnclosingExpression();
+ if (enclosingExpression != null) { // expr.new IDENT|
+ TypeMirror site = at.trees().getTypeMirror(new TreePath(tp, enclosingExpression));
+ filter = filter.and(el -> el.getEnclosingElement().getKind() == ElementKind.CLASS && !el.getEnclosingElement().getModifiers().contains(Modifier.STATIC));
+ addElements(membersOf(at, membersOf(at, site, false)), filter, smartFilter, result);
+ } else {
+ addScopeElements(at, scope, listEnclosed, filter, smartFilter, result);
+ }
+ break;
+ }
+ if (isThrowsClause(tp)) {
+ Predicate<Element> accept = accessibility.and(STATIC_ONLY)
+ .and(IS_PACKAGE.or(IS_CLASS).or(IS_INTERFACE));
+ addScopeElements(at, scope, IDENTITY, accept, IS_PACKAGE.negate().and(smartTypeFilter), result);
+ break;
+ }
+ ImportTree it = findImport(tp);
+ if (it != null) {
+ // the context of the identifier is an import, look for
+ // package names that start with the identifier.
+ // If and when Java allows imports from the default
+ // package to the the default package which would allow
+ // JShell to change to use the default package, and that
+ // change is done, then this should use some variation
+ // of membersOf(at, at.getElements().getPackageElement("").asType(), false)
+ addElements(listPackages(at, ""),
+ it.isStatic()
+ ? STATIC_ONLY.and(accessibility)
+ : accessibility,
+ smartFilter, result);
+ }
+ break;
+ case CLASS: {
+ Predicate<Element> accept = accessibility.and(IS_TYPE);
+ addScopeElements(at, scope, IDENTITY, accept, smartFilter, result);
+ addElements(primitivesOrVoid(at), TRUE, smartFilter, result);
break;
}
- ImportTree it = findImport(tp);
- if (it != null) {
- // the context of the identifier is an import, look for
- // package names that start with the identifier.
- // If and when Java allows imports from the default
- // package to the the default package which would allow
- // JShell to change to use the default package, and that
- // change is done, then this should use some variation
- // of membersOf(at, at.getElements().getPackageElement("").asType(), false)
- addElements(listPackages(at, ""),
- it.isStatic()
- ? STATIC_ONLY.and(accessibility)
- : accessibility,
- smartFilter, result);
- }
- break;
- case CLASS: {
- Predicate<Element> accept = accessibility.and(IS_TYPE);
- addScopeElements(at, scope, IDENTITY, accept, smartFilter, result);
- addElements(primitivesOrVoid(at), TRUE, smartFilter, result);
- break;
- }
- case BLOCK:
- case EMPTY_STATEMENT:
- case ERRONEOUS: {
- boolean staticOnly = ReplResolve.isStatic(((JavacScope)scope).getEnv());
- Predicate<Element> accept = accessibility.and(staticOnly ? STATIC_ONLY : TRUE);
- if (isClass(tp)) {
- ClassTree clazz = (ClassTree) tp.getParentPath().getLeaf();
- if (clazz.getExtendsClause() == tp.getLeaf()) {
- accept = accept.and(IS_TYPE);
- smartFilter = smartFilter.and(el -> el.getKind() == ElementKind.CLASS);
- } else {
- Predicate<Element> f = smartFilterFromList(at, tp, clazz.getImplementsClause(), tp.getLeaf());
+ case BLOCK:
+ case EMPTY_STATEMENT:
+ case ERRONEOUS: {
+ boolean staticOnly = ReplResolve.isStatic(((JavacScope)scope).getEnv());
+ Predicate<Element> accept = accessibility.and(staticOnly ? STATIC_ONLY : TRUE);
+ if (isClass(tp)) {
+ ClassTree clazz = (ClassTree) tp.getParentPath().getLeaf();
+ if (clazz.getExtendsClause() == tp.getLeaf()) {
+ accept = accept.and(IS_TYPE);
+ smartFilter = smartFilter.and(el -> el.getKind() == ElementKind.CLASS);
+ } else {
+ Predicate<Element> f = smartFilterFromList(at, tp, clazz.getImplementsClause(), tp.getLeaf());
+ if (f != null) {
+ accept = accept.and(IS_TYPE);
+ smartFilter = f.and(el -> el.getKind() == ElementKind.INTERFACE);
+ }
+ }
+ } else if (isTypeParameter(tp)) {
+ TypeParameterTree tpt = (TypeParameterTree) tp.getParentPath().getLeaf();
+ Predicate<Element> f = smartFilterFromList(at, tp, tpt.getBounds(), tp.getLeaf());
if (f != null) {
accept = accept.and(IS_TYPE);
- smartFilter = f.and(el -> el.getKind() == ElementKind.INTERFACE);
+ smartFilter = f;
+ if (!tpt.getBounds().isEmpty() && tpt.getBounds().get(0) != tp.getLeaf()) {
+ smartFilter = smartFilter.and(el -> el.getKind() == ElementKind.INTERFACE);
+ }
+ }
+ } else if (isVariable(tp)) {
+ VariableTree var = (VariableTree) tp.getParentPath().getLeaf();
+ if (var.getType() == tp.getLeaf()) {
+ accept = accept.and(IS_TYPE);
}
}
- } else if (isTypeParameter(tp)) {
- TypeParameterTree tpt = (TypeParameterTree) tp.getParentPath().getLeaf();
- Predicate<Element> f = smartFilterFromList(at, tp, tpt.getBounds(), tp.getLeaf());
- if (f != null) {
- accept = accept.and(IS_TYPE);
- smartFilter = f;
- if (!tpt.getBounds().isEmpty() && tpt.getBounds().get(0) != tp.getLeaf()) {
- smartFilter = smartFilter.and(el -> el.getKind() == ElementKind.INTERFACE);
- }
- }
- } else if (isVariable(tp)) {
- VariableTree var = (VariableTree) tp.getParentPath().getLeaf();
- if (var.getType() == tp.getLeaf()) {
- accept = accept.and(IS_TYPE);
- }
- }
- addScopeElements(at, scope, IDENTITY, accept, smartFilter, result);
+ addScopeElements(at, scope, IDENTITY, accept, smartFilter, result);
- Tree parent = tp.getParentPath().getLeaf();
- switch (parent.getKind()) {
- case VARIABLE:
- accept = ((VariableTree)parent).getType() == tp.getLeaf() ?
- IS_VOID.negate() :
- TRUE;
- break;
- case PARAMETERIZED_TYPE: // TODO: JEP 218: Generics over Primitive Types
- case TYPE_PARAMETER:
- case CLASS:
- case INTERFACE:
- case ENUM:
- accept = FALSE;
- break;
- default:
- accept = TRUE;
- break;
+ Tree parent = tp.getParentPath().getLeaf();
+ switch (parent.getKind()) {
+ case VARIABLE:
+ accept = ((VariableTree)parent).getType() == tp.getLeaf() ?
+ IS_VOID.negate() :
+ TRUE;
+ break;
+ case PARAMETERIZED_TYPE: // TODO: JEP 218: Generics over Primitive Types
+ case TYPE_PARAMETER:
+ case CLASS:
+ case INTERFACE:
+ case ENUM:
+ accept = FALSE;
+ break;
+ default:
+ accept = TRUE;
+ break;
+ }
+ addElements(primitivesOrVoid(at), accept, smartFilter, result);
+ break;
}
- addElements(primitivesOrVoid(at), accept, smartFilter, result);
- break;
}
}
- }
- anchor[0] = cursor;
- return result;
+ anchor[0] = cursor;
+ return result;
+ });
}
private static final Set<Kind> CLASS_KINDS = EnumSet.of(
@@ -1167,78 +1169,79 @@
return Collections.emptyList();
OuterWrap codeWrap = proc.outerMap.wrapInTrialClass(Wrap.methodWrap(code));
- AnalyzeTask at = proc.taskFactory.new AnalyzeTask(codeWrap, keepParameterNames);
- SourcePositions sp = at.trees().getSourcePositions();
- CompilationUnitTree topLevel = at.firstCuTree();
- TreePath tp = pathFor(topLevel, sp, codeWrap.snippetIndexToWrapIndex(cursor));
-
- if (tp == null)
- return Collections.emptyList();
-
- TreePath prevPath = null;
- while (tp != null && tp.getLeaf().getKind() != Kind.METHOD_INVOCATION &&
- tp.getLeaf().getKind() != Kind.NEW_CLASS && tp.getLeaf().getKind() != Kind.IDENTIFIER &&
- tp.getLeaf().getKind() != Kind.MEMBER_SELECT) {
- prevPath = tp;
- tp = tp.getParentPath();
- }
+ return proc.taskFactory.analyze(codeWrap, List.of(keepParameterNames), at -> {
+ SourcePositions sp = at.trees().getSourcePositions();
+ CompilationUnitTree topLevel = at.firstCuTree();
+ TreePath tp = pathFor(topLevel, sp, codeWrap.snippetIndexToWrapIndex(cursor));
- if (tp == null)
- return Collections.emptyList();
-
- Stream<Element> elements;
- Iterable<Pair<ExecutableElement, ExecutableType>> candidates;
- List<? extends ExpressionTree> arguments;
+ if (tp == null)
+ return Collections.emptyList();
- if (tp.getLeaf().getKind() == Kind.METHOD_INVOCATION || tp.getLeaf().getKind() == Kind.NEW_CLASS) {
- if (tp.getLeaf().getKind() == Kind.METHOD_INVOCATION) {
- MethodInvocationTree mit = (MethodInvocationTree) tp.getLeaf();
- candidates = methodCandidates(at, tp);
- arguments = mit.getArguments();
- } else {
- NewClassTree nct = (NewClassTree) tp.getLeaf();
- candidates = newClassCandidates(at, tp);
- arguments = nct.getArguments();
+ TreePath prevPath = null;
+ while (tp != null && tp.getLeaf().getKind() != Kind.METHOD_INVOCATION &&
+ tp.getLeaf().getKind() != Kind.NEW_CLASS && tp.getLeaf().getKind() != Kind.IDENTIFIER &&
+ tp.getLeaf().getKind() != Kind.MEMBER_SELECT) {
+ prevPath = tp;
+ tp = tp.getParentPath();
}
- if (!isEmptyArgumentsContext(arguments)) {
- List<TypeMirror> actuals = computeActualInvocationTypes(at, arguments, prevPath);
- List<TypeMirror> fullActuals = actuals != null ? actuals : Collections.emptyList();
+ if (tp == null)
+ return Collections.emptyList();
+
+ Stream<Element> elements;
+ Iterable<Pair<ExecutableElement, ExecutableType>> candidates;
+ List<? extends ExpressionTree> arguments;
+
+ if (tp.getLeaf().getKind() == Kind.METHOD_INVOCATION || tp.getLeaf().getKind() == Kind.NEW_CLASS) {
+ if (tp.getLeaf().getKind() == Kind.METHOD_INVOCATION) {
+ MethodInvocationTree mit = (MethodInvocationTree) tp.getLeaf();
+ candidates = methodCandidates(at, tp);
+ arguments = mit.getArguments();
+ } else {
+ NewClassTree nct = (NewClassTree) tp.getLeaf();
+ candidates = newClassCandidates(at, tp);
+ arguments = nct.getArguments();
+ }
- candidates =
- this.filterExecutableTypesByArguments(at, candidates, fullActuals)
- .stream()
- .filter(method -> parameterType(method.fst, method.snd, fullActuals.size(), true).findAny().isPresent())
- .collect(Collectors.toList());
- }
+ if (!isEmptyArgumentsContext(arguments)) {
+ List<TypeMirror> actuals = computeActualInvocationTypes(at, arguments, prevPath);
+ List<TypeMirror> fullActuals = actuals != null ? actuals : Collections.emptyList();
+
+ candidates =
+ this.filterExecutableTypesByArguments(at, candidates, fullActuals)
+ .stream()
+ .filter(method -> parameterType(method.fst, method.snd, fullActuals.size(), true).findAny().isPresent())
+ .collect(Collectors.toList());
+ }
- elements = Util.stream(candidates).map(method -> method.fst);
- } else if (tp.getLeaf().getKind() == Kind.IDENTIFIER || tp.getLeaf().getKind() == Kind.MEMBER_SELECT) {
- Element el = at.trees().getElement(tp);
+ elements = Util.stream(candidates).map(method -> method.fst);
+ } else if (tp.getLeaf().getKind() == Kind.IDENTIFIER || tp.getLeaf().getKind() == Kind.MEMBER_SELECT) {
+ Element el = at.trees().getElement(tp);
- if (el == null ||
- el.asType().getKind() == TypeKind.ERROR ||
- (el.getKind() == ElementKind.PACKAGE && el.getEnclosedElements().isEmpty())) {
- //erroneous element:
+ if (el == null ||
+ el.asType().getKind() == TypeKind.ERROR ||
+ (el.getKind() == ElementKind.PACKAGE && el.getEnclosedElements().isEmpty())) {
+ //erroneous element:
+ return Collections.emptyList();
+ }
+
+ elements = Stream.of(el);
+ } else {
return Collections.emptyList();
}
- elements = Stream.of(el);
- } else {
- return Collections.emptyList();
- }
-
- List<Documentation> result = Collections.emptyList();
+ List<Documentation> result = Collections.emptyList();
- try (JavadocHelper helper = JavadocHelper.create(at.task, findSources())) {
- result = elements.map(el -> constructDocumentation(at, helper, el, computeJavadoc))
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
- } catch (IOException ex) {
- proc.debug(ex, "JavadocHelper.close()");
- }
+ try (JavadocHelper helper = JavadocHelper.create(at.task, findSources())) {
+ result = elements.map(el -> constructDocumentation(at, helper, el, computeJavadoc))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ } catch (IOException ex) {
+ proc.debug(ex, "JavadocHelper.close()");
+ }
- return result;
+ return result;
+ });
}
private Documentation constructDocumentation(AnalyzeTask at, JavadocHelper helper, Element el, boolean computeJavadoc) {
@@ -1494,51 +1497,52 @@
@Override
public QualifiedNames listQualifiedNames(String code, int cursor) {
- code = code.substring(0, cursor);
- if (code.trim().isEmpty()) {
+ String codeFin = code.substring(0, cursor);
+ if (codeFin.trim().isEmpty()) {
return new QualifiedNames(Collections.emptyList(), -1, true, false);
}
OuterWrap codeWrap;
- switch (guessKind(code)) {
+ switch (guessKind(codeFin)) {
case IMPORT:
return new QualifiedNames(Collections.emptyList(), -1, true, false);
case METHOD:
- codeWrap = proc.outerMap.wrapInTrialClass(Wrap.classMemberWrap(code));
+ codeWrap = proc.outerMap.wrapInTrialClass(Wrap.classMemberWrap(codeFin));
break;
default:
- codeWrap = proc.outerMap.wrapInTrialClass(Wrap.methodWrap(code));
+ codeWrap = proc.outerMap.wrapInTrialClass(Wrap.methodWrap(codeFin));
break;
}
- AnalyzeTask at = proc.taskFactory.new AnalyzeTask(codeWrap);
- SourcePositions sp = at.trees().getSourcePositions();
- CompilationUnitTree topLevel = at.firstCuTree();
- TreePath tp = pathFor(topLevel, sp, codeWrap.snippetIndexToWrapIndex(code.length()));
- if (tp.getLeaf().getKind() != Kind.IDENTIFIER) {
- return new QualifiedNames(Collections.emptyList(), -1, true, false);
- }
- Scope scope = at.trees().getScope(tp);
- TypeMirror type = at.trees().getTypeMirror(tp);
- Element el = at.trees().getElement(tp);
+ return proc.taskFactory.analyze(codeWrap, at -> {
+ SourcePositions sp = at.trees().getSourcePositions();
+ CompilationUnitTree topLevel = at.firstCuTree();
+ TreePath tp = pathFor(topLevel, sp, codeWrap.snippetIndexToWrapIndex(codeFin.length()));
+ if (tp.getLeaf().getKind() != Kind.IDENTIFIER) {
+ return new QualifiedNames(Collections.emptyList(), -1, true, false);
+ }
+ Scope scope = at.trees().getScope(tp);
+ TypeMirror type = at.trees().getTypeMirror(tp);
+ Element el = at.trees().getElement(tp);
- boolean erroneous = (type.getKind() == TypeKind.ERROR && el.getKind() == ElementKind.CLASS) ||
- (el.getKind() == ElementKind.PACKAGE && el.getEnclosedElements().isEmpty());
- String simpleName = ((IdentifierTree) tp.getLeaf()).getName().toString();
- boolean upToDate;
- List<String> result;
+ boolean erroneous = (type.getKind() == TypeKind.ERROR && el.getKind() == ElementKind.CLASS) ||
+ (el.getKind() == ElementKind.PACKAGE && el.getEnclosedElements().isEmpty());
+ String simpleName = ((IdentifierTree) tp.getLeaf()).getName().toString();
+ boolean upToDate;
+ List<String> result;
- synchronized (currentIndexes) {
- upToDate = classpathVersion == indexVersion;
- result = currentIndexes.values()
- .stream()
- .flatMap(idx -> idx.classSimpleName2FQN.getOrDefault(simpleName,
- Collections.emptyList()).stream())
- .distinct()
- .filter(fqn -> isAccessible(at, scope, fqn))
- .sorted()
- .collect(Collectors.toList());
- }
+ synchronized (currentIndexes) {
+ upToDate = classpathVersion == indexVersion;
+ result = currentIndexes.values()
+ .stream()
+ .flatMap(idx -> idx.classSimpleName2FQN.getOrDefault(simpleName,
+ Collections.emptyList()).stream())
+ .distinct()
+ .filter(fqn -> isAccessible(at, scope, fqn))
+ .sorted()
+ .collect(Collectors.toList());
+ }
- return new QualifiedNames(result, simpleName.length(), upToDate, !erroneous);
+ return new QualifiedNames(result, simpleName.length(), upToDate, !erroneous);
+ });
}
private boolean isAccessible(AnalyzeTask at, Scope scope, String fqn) {
--- a/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java Mon Oct 16 18:15:41 2017 +0530
+++ b/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java Fri Sep 01 14:04:20 2017 +0200
@@ -29,10 +29,8 @@
import com.sun.source.tree.Tree;
import com.sun.source.util.Trees;
import com.sun.tools.javac.api.JavacTaskImpl;
-import com.sun.tools.javac.api.JavacTool;
import com.sun.tools.javac.util.Context;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
@@ -62,18 +60,28 @@
import jdk.jshell.MemoryFileManager.SourceMemoryJavaFileObject;
import java.lang.Runtime.Version;
import java.nio.CharBuffer;
+import java.util.function.BiFunction;
import com.sun.source.tree.Tree.Kind;
+import com.sun.source.util.TaskEvent;
+import com.sun.source.util.TaskListener;
+import com.sun.tools.javac.api.JavacTaskPool;
+import com.sun.tools.javac.code.ClassFinder;
import com.sun.tools.javac.code.Kinds;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symbol.PackageSymbol;
import com.sun.tools.javac.code.Symbol.VarSymbol;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.comp.Attr;
import com.sun.tools.javac.parser.Parser;
+import com.sun.tools.javac.parser.ParserFactory;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCTypeCast;
import com.sun.tools.javac.tree.JCTree.Tag;
-import com.sun.tools.javac.util.Context.Factory;
+import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Log.DiscardDiagnosticHandler;
+import com.sun.tools.javac.util.Names;
import jdk.jshell.Snippet.Status;
/**
@@ -101,6 +109,7 @@
}
this.fileManager = new MemoryFileManager(
compiler.getStandardFileManager(null, null, null), state);
+ initTaskPool();
}
void addToClasspath(String path) {
@@ -108,27 +117,130 @@
List<String> args = new ArrayList<>();
args.add(classpath);
fileManager().handleOption("-classpath", args.iterator());
+ initTaskPool();
}
MemoryFileManager fileManager() {
return fileManager;
}
+ public <Z> Z parse(String source,
+ boolean forceExpression,
+ Worker<ParseTask, Z> worker) {
+ StringSourceHandler sh = new StringSourceHandler();
+ return runTask(Stream.of(source),
+ sh,
+ List.of("-XDallowStringFolding=false", "-proc:none",
+ "-XDneedsReplParserFactory=" + forceExpression),
+ (jti, diagnostics) -> new ParseTask(sh, jti, diagnostics, forceExpression),
+ worker);
+ }
+
+ public <Z> Z analyze(OuterWrap wrap,
+ Worker<AnalyzeTask, Z> worker) {
+ return analyze(Collections.singletonList(wrap), worker);
+ }
+
+ public <Z> Z analyze(OuterWrap wrap,
+ List<String> extraArgs,
+ Worker<AnalyzeTask, Z> worker) {
+ return analyze(Collections.singletonList(wrap), extraArgs, worker);
+ }
+
+ public <Z> Z analyze(Collection<OuterWrap> wraps,
+ Worker<AnalyzeTask, Z> worker) {
+ return analyze(wraps, Collections.emptyList(), worker);
+ }
+
+ public <Z> Z analyze(Collection<OuterWrap> wraps,
+ List<String> extraArgs,
+ Worker<AnalyzeTask, Z> worker) {
+ WrapSourceHandler sh = new WrapSourceHandler();
+ List<String> allOptions = new ArrayList<>();
+
+ allOptions.add("--should-stop:at=FLOW");
+ allOptions.add("-Xlint:unchecked");
+ allOptions.add("-proc:none");
+ allOptions.addAll(extraArgs);
+
+ return runTask(wraps.stream(),
+ sh,
+ allOptions,
+ (jti, diagnostics) -> new AnalyzeTask(sh, jti, diagnostics),
+ worker);
+ }
+
+ public <Z> Z compile(Collection<OuterWrap> wraps,
+ Worker<CompileTask, Z> worker) {
+ WrapSourceHandler sh = new WrapSourceHandler();
+
+ return runTask(wraps.stream(),
+ sh,
+ List.of("-Xlint:unchecked", "-proc:none", "-parameters"),
+ (jti, diagnostics) -> new CompileTask(sh, jti, diagnostics),
+ worker);
+ }
+
+ private <S, T extends BaseTask, Z> Z runTask(Stream<S> inputs,
+ SourceHandler<S> sh,
+ List<String> options,
+ BiFunction<JavacTaskImpl, DiagnosticCollector<JavaFileObject>, T> creator,
+ Worker<T, Z> worker) {
+ List<String> allOptions = new ArrayList<>(options.size() + state.extraCompilerOptions.size());
+ allOptions.addAll(options);
+ allOptions.addAll(state.extraCompilerOptions);
+ Iterable<? extends JavaFileObject> compilationUnits = inputs
+ .map(in -> sh.sourceToFileObject(fileManager, in))
+ .collect(Collectors.toList());
+ DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
+ return javacTaskPool.getTask(null, fileManager, diagnostics, allOptions, null,
+ compilationUnits, task -> {
+ JavacTaskImpl jti = (JavacTaskImpl) task;
+ Context context = jti.getContext();
+ jti.addTaskListener(new TaskListenerImpl(context, state));
+ try {
+ return worker.withTask(creator.apply(jti, diagnostics));
+ } finally {
+ //additional cleanup: purge the REPL package:
+ Symtab syms = Symtab.instance(context);
+ Names names = Names.instance(context);
+ PackageSymbol repl = syms.getPackage(syms.unnamedModule, names.fromString(Util.REPL_PACKAGE));
+ if (repl != null) {
+ for (ClassSymbol clazz : syms.getAllClasses()) {
+ if (clazz.packge() == repl) {
+ syms.removeClass(syms.unnamedModule, clazz.flatName());
+ }
+ }
+ repl.members_field = null;
+ repl.completer = ClassFinder.instance(context).getCompleter();
+ }
+ }
+ });
+ }
+
+ interface Worker<T extends BaseTask, Z> {
+ public Z withTask(T task);
+ }
+
// Parse a snippet and return our parse task handler
- ParseTask parse(final String source) {
- ParseTask pt = state.taskFactory.new ParseTask(source, false);
- if (!pt.units().isEmpty()
- && pt.units().get(0).getKind() == Kind.EXPRESSION_STATEMENT
- && pt.getDiagnostics().hasOtherThanNotStatementErrors()) {
- // It failed, it may be an expression being incorrectly
- // parsed as having a leading type variable, example: a < b
- // Try forcing interpretation as an expression
- ParseTask ept = state.taskFactory.new ParseTask(source, true);
- if (!ept.getDiagnostics().hasOtherThanNotStatementErrors()) {
- return ept;
+ <Z> Z parse(final String source, Worker<ParseTask, Z> worker) {
+ return parse(source, false, pt -> {
+ if (!pt.units().isEmpty()
+ && pt.units().get(0).getKind() == Kind.EXPRESSION_STATEMENT
+ && pt.getDiagnostics().hasOtherThanNotStatementErrors()) {
+ // It failed, it may be an expression being incorrectly
+ // parsed as having a leading type variable, example: a < b
+ // Try forcing interpretation as an expression
+ return parse(source, true, ept -> {
+ if (!ept.getDiagnostics().hasOtherThanNotStatementErrors()) {
+ return worker.withTask(ept);
+ } else {
+ return worker.withTask(pt);
+ }
+ });
}
- }
- return pt;
+ return worker.withTask(pt);
+ });
}
private interface SourceHandler<T> {
@@ -210,11 +322,12 @@
private final Iterable<? extends CompilationUnitTree> cuts;
private final List<? extends Tree> units;
- ParseTask(final String source, final boolean forceExpression) {
- super(Stream.of(source),
- new StringSourceHandler(),
- "-XDallowStringFolding=false", "-proc:none");
- ReplParserFactory.preRegister(getContext(), forceExpression);
+ private ParseTask(SourceHandler<String> sh,
+ JavacTaskImpl task,
+ DiagnosticCollector<JavaFileObject> diagnostics,
+ boolean forceExpression) {
+ super(sh, task, diagnostics);
+ ReplParserFactory.preRegister(context, forceExpression);
cuts = parse();
units = Util.stream(cuts)
.flatMap(cut -> {
@@ -249,22 +362,10 @@
private final Iterable<? extends CompilationUnitTree> cuts;
- AnalyzeTask(final OuterWrap wrap, String... extraArgs) {
- this(Collections.singletonList(wrap), extraArgs);
- }
-
- AnalyzeTask(final Collection<OuterWrap> wraps, String... extraArgs) {
- this(wraps.stream(),
- new WrapSourceHandler(),
- Util.join(new String[] {
- "--should-stop:at=FLOW", "-Xlint:unchecked",
- "-proc:none"
- }, extraArgs));
- }
-
- private <T>AnalyzeTask(final Stream<T> stream, SourceHandler<T> sourceHandler,
- String... extraOptions) {
- super(stream, sourceHandler, extraOptions);
+ private AnalyzeTask(SourceHandler<OuterWrap> sh,
+ JavacTaskImpl task,
+ DiagnosticCollector<JavaFileObject> diagnostics) {
+ super(sh, task, diagnostics);
cuts = analyze();
}
@@ -299,9 +400,10 @@
private final Map<OuterWrap, List<OutputMemoryJavaFileObject>> classObjs = new HashMap<>();
- CompileTask(final Collection<OuterWrap> wraps) {
- super(wraps.stream(), new WrapSourceHandler(),
- "-Xlint:unchecked", "-proc:none", "-parameters");
+ CompileTask(SourceHandler<OuterWrap>sh,
+ JavacTaskImpl jti,
+ DiagnosticCollector<JavaFileObject> diagnostics) {
+ super(sh, jti, diagnostics);
}
boolean compile() {
@@ -346,32 +448,30 @@
}
}
+ private JavacTaskPool javacTaskPool;
+
+ private void initTaskPool() {
+ javacTaskPool = new JavacTaskPool(5);
+ }
+
abstract class BaseTask {
- final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
+ final DiagnosticCollector<JavaFileObject> diagnostics;
final JavacTaskImpl task;
private DiagList diags = null;
private final SourceHandler<?> sourceHandler;
- final Context context = new Context();
+ final Context context;
private Types types;
private JavacMessages messages;
private Trees trees;
- private <T>BaseTask(Stream<T> inputs,
- //BiFunction<MemoryFileManager, T, JavaFileObject> sfoCreator,
- SourceHandler<T> sh,
- String... extraOptions) {
+ private <T>BaseTask(SourceHandler<T> sh,
+ JavacTaskImpl task,
+ DiagnosticCollector<JavaFileObject> diagnostics) {
this.sourceHandler = sh;
- List<String> options = new ArrayList<>(extraOptions.length + state.extraCompilerOptions.size());
- options.addAll(Arrays.asList(extraOptions));
- options.addAll(state.extraCompilerOptions);
- Iterable<? extends JavaFileObject> compilationUnits = inputs
- .map(in -> sh.sourceToFileObject(fileManager, in))
- .collect(Collectors.toList());
- JShellJavaCompiler.preRegister(context, state);
- this.task = (JavacTaskImpl) ((JavacTool) compiler).getTask(null,
- fileManager, diagnostics, options, null,
- compilationUnits, context);
+ this.task = task;
+ context = task.getContext();
+ this.diagnostics = diagnostics;
}
abstract Iterable<? extends CompilationUnitTree> cuTrees();
@@ -478,32 +578,36 @@
}
}
- private static final class JShellJavaCompiler extends com.sun.tools.javac.main.JavaCompiler {
+ private static final class TaskListenerImpl implements TaskListener {
- public static void preRegister(Context c, JShell state) {
- c.put(compilerKey, (Factory<com.sun.tools.javac.main.JavaCompiler>) i -> new JShellJavaCompiler(i, state));
- }
-
+ private final Context context;
private final JShell state;
- public JShellJavaCompiler(Context context, JShell state) {
- super(context);
+ public TaskListenerImpl(Context context, JShell state) {
+ this.context = context;
this.state = state;
}
@Override
- public void processAnnotations(com.sun.tools.javac.util.List<JCCompilationUnit> roots, Collection<String> classnames) {
- super.processAnnotations(roots, classnames);
+ public void finished(TaskEvent e) {
+ if (e.getKind() != TaskEvent.Kind.ENTER)
+ return ;
state.maps
.snippetList()
.stream()
.filter(s -> s.status() == Status.VALID)
.filter(s -> s.kind() == Snippet.Kind.VAR)
.filter(s -> s.subKind() == Snippet.SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND)
- .forEach(s -> setVariableType(roots, (VarSnippet) s));
+ .forEach(s -> setVariableType((JCCompilationUnit) e.getCompilationUnit(), (VarSnippet) s));
}
- private void setVariableType(com.sun.tools.javac.util.List<JCCompilationUnit> roots, VarSnippet s) {
+ private void setVariableType(JCCompilationUnit root, VarSnippet s) {
+ Symtab syms = Symtab.instance(context);
+ Names names = Names.instance(context);
+ Log log = Log.instance(context);
+ ParserFactory parserFactory = ParserFactory.instance(context);
+ Attr attr = Attr.instance(context);
+
ClassSymbol clazz = syms.getClass(syms.unnamedModule, names.fromString(s.classFullName()));
if (clazz == null || !clazz.isCompleted())
return;
@@ -520,7 +624,7 @@
JCTypeCast tree = (JCTypeCast) expr;
if (tree.clazz.hasTag(Tag.TYPEINTERSECTION)) {
field.type = attr.attribType(tree.clazz,
- ((JCClassDecl) roots.head.getTypeDecls().head).sym);
+ ((JCClassDecl) root.getTypeDecls().head).sym);
}
}
} finally {
--- a/test/langtools/tools/javac/T7093325.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/T7093325.java Fri Sep 01 14:04:20 2017 +0200
@@ -130,9 +130,9 @@
@Override
public void doWork() throws IOException {
- verifyBytecode(newCompilationTask()
+ newCompilationTask()
.withSourceFromTemplate(source_template)
- .generate());
+ .generate(this::verifyBytecode);
}
void verifyBytecode(Result<Iterable<? extends JavaFileObject>> result) {
--- a/test/langtools/tools/javac/cast/intersection/IntersectionTypeCastTest.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/cast/intersection/IntersectionTypeCastTest.java Fri Sep 01 14:04:20 2017 +0200
@@ -254,9 +254,9 @@
@Override
public void doWork() throws IOException {
- check(newCompilationTask()
+ newCompilationTask()
.withSourceFromTemplate(bodyTemplate)
- .analyze());
+ .analyze(this::check);
}
String bodyTemplate = "class Test {\n" +
--- a/test/langtools/tools/javac/defaultMethods/static/hiding/InterfaceMethodHidingTest.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/defaultMethods/static/hiding/InterfaceMethodHidingTest.java Fri Sep 01 14:04:20 2017 +0200
@@ -154,10 +154,10 @@
@Override
public void doWork() throws IOException {
- check(newCompilationTask()
+ newCompilationTask()
.withOption("-XDallowStaticInterfaceMethods")
.withSourceFromTemplate(template, this::returnExpr)
- .analyze());
+ .analyze(this::check);
}
ComboParameter returnExpr(String name) {
--- a/test/langtools/tools/javac/defaultMethods/super/TestDefaultSuperCall.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/defaultMethods/super/TestDefaultSuperCall.java Fri Sep 01 14:04:20 2017 +0200
@@ -303,9 +303,9 @@
@Override
public void doWork() throws IOException {
- check(newCompilationTask()
+ newCompilationTask()
.withSourceFromTemplate(template, this::methodName)
- .analyze());
+ .analyze(this::check);
}
ComboParameter methodName(String parameterName) {
--- a/test/langtools/tools/javac/failover/CheckAttributedTree.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/failover/CheckAttributedTree.java Fri Sep 01 14:04:20 2017 +0200
@@ -67,6 +67,7 @@
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiConsumer;
import javax.lang.model.element.Element;
import javax.swing.DefaultComboBoxModel;
@@ -287,61 +288,54 @@
errWriter.println(file);
fileCount.incrementAndGet();
NPETester p = new NPETester();
- p.test(read(file));
- } catch (AttributionException e) {
+ readAndCheck(file, p::test);
+ } catch (Throwable t) {
if (!quiet) {
- error("Error attributing " + file + "\n" + e.getMessage());
+ error("Error checking " + file + "\n" + t.getMessage());
}
- } catch (IOException e) {
- error("Error reading " + file + ": " + e);
}
}
/**
- * Read a file.
+ * Read and check a file.
* @param file the file to be read
* @return the tree for the content of the file
* @throws IOException if any IO errors occur
* @throws AttributionException if any errors occur while analyzing the file
*/
- List<Pair<JCCompilationUnit, JCTree>> read(File file) throws IOException, AttributionException {
- try {
- Iterable<? extends JavaFileObject> files = fileManager().getJavaFileObjects(file);
- final List<Element> analyzedElems = new ArrayList<>();
- final List<CompilationUnitTree> trees = new ArrayList<>();
- Iterable<? extends Element> elems = newCompilationTask()
- .withWriter(pw)
- .withOption("--should-stop:at=ATTR")
- .withOption("-XDverboseCompilePolicy")
- .withSource(files.iterator().next())
- .withListener(new TaskListener() {
- public void started(TaskEvent e) {
- if (e.getKind() == TaskEvent.Kind.ANALYZE)
- analyzedElems.add(e.getTypeElement());
- }
+ void readAndCheck(File file, BiConsumer<JCCompilationUnit, JCTree> c) throws IOException {
+ Iterable<? extends JavaFileObject> files = fileManager().getJavaFileObjects(file);
+ final List<Element> analyzedElems = new ArrayList<>();
+ final List<CompilationUnitTree> trees = new ArrayList<>();
+ newCompilationTask()
+ .withWriter(pw)
+ .withOption("--should-stop:at=ATTR")
+ .withOption("-XDverboseCompilePolicy")
+ .withSource(files.iterator().next())
+ .withListener(new TaskListener() {
+ public void started(TaskEvent e) {
+ if (e.getKind() == TaskEvent.Kind.ANALYZE)
+ analyzedElems.add(e.getTypeElement());
+ }
- public void finished(TaskEvent e) {
- if (e.getKind() == Kind.PARSE)
- trees.add(e.getCompilationUnit());
- }
- }).analyze().get();
+ public void finished(TaskEvent e) {
+ if (e.getKind() == Kind.PARSE)
+ trees.add(e.getCompilationUnit());
+ }
+ }).analyze(res -> {
+ Iterable<? extends Element> elems = res.get();
if (!elems.iterator().hasNext())
- throw new AttributionException("No results from analyze");
- List<Pair<JCCompilationUnit, JCTree>> res = new ArrayList<>();
+ throw new AssertionError("No results from analyze");
for (CompilationUnitTree t : trees) {
JCCompilationUnit cu = (JCCompilationUnit)t;
for (JCTree def : cu.defs) {
if (def.hasTag(CLASSDEF) &&
analyzedElems.contains(((JCTree.JCClassDecl)def).sym)) {
- res.add(new Pair<>(cu, def));
+ c.accept(cu, def);
}
}
}
- return res;
- }
- catch (Throwable t) {
- throw new AttributionException("Exception while attributing file: " + file);
- }
+ });
}
/**
@@ -361,13 +355,11 @@
* left uninitialized after attribution
*/
private class NPETester extends TreeScanner {
- void test(List<Pair<JCCompilationUnit, JCTree>> trees) {
- for (Pair<JCCompilationUnit, JCTree> p : trees) {
- sourcefile = p.fst.sourcefile;
- endPosTable = p.fst.endPositions;
- encl = new Info(p.snd, endPosTable);
- p.snd.accept(this);
- }
+ void test(JCCompilationUnit cut, JCTree tree) {
+ sourcefile = cut.sourcefile;
+ endPosTable = cut.endPositions;
+ encl = new Info(tree, endPosTable);
+ tree.accept(this);
}
@Override
@@ -537,21 +529,6 @@
}
/**
- * Thrown when errors are found parsing a java file.
- */
- private static class ParseException extends Exception {
- ParseException(String msg) {
- super(msg);
- }
- }
-
- private static class AttributionException extends Exception {
- AttributionException(String msg) {
- super(msg);
- }
- }
-
- /**
* GUI viewer for issues found by TreePosTester. The viewer provides a drop
* down list for selecting error conditions, a header area providing details
* about an error, and a text area with the ranges of text highlighted as
--- a/test/langtools/tools/javac/generics/diamond/7046778/DiamondAndInnerClassTest.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/generics/diamond/7046778/DiamondAndInnerClassTest.java Fri Sep 01 14:04:20 2017 +0200
@@ -238,9 +238,9 @@
@Override
public void doWork() throws IOException {
- check(newCompilationTask()
+ newCompilationTask()
.withSourceFromTemplate("#{DECL}")
- .analyze());
+ .analyze(this::check);
}
void check(Result<?> res) {
--- a/test/langtools/tools/javac/generics/inference/8176534/TestUncheckedCalls.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/generics/inference/8176534/TestUncheckedCalls.java Fri Sep 01 14:04:20 2017 +0200
@@ -241,9 +241,9 @@
@Override
public void doWork() throws Throwable {
- check(newCompilationTask()
+ newCompilationTask()
.withSourceFromTemplate(sourceTemplate)
- .analyze());
+ .analyze(this::check);
}
void check(Result<Iterable<? extends Element>> result) {
--- a/test/langtools/tools/javac/generics/rawOverride/7062745/GenericOverrideTest.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/generics/rawOverride/7062745/GenericOverrideTest.java Fri Sep 01 14:04:20 2017 +0200
@@ -189,11 +189,11 @@
@Override
public void doWork() throws IOException {
- check(newCompilationTask()
+ newCompilationTask()
.withOption("-XDuseUnsharedTable") //this test relies on predictable name indexes!
.withOptions(level.opts)
.withSourceFromTemplate(template)
- .analyze());
+ .analyze(this::check);
}
void check(Result<?> res) {
--- a/test/langtools/tools/javac/lambda/FunctionalInterfaceConversionTest.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/lambda/FunctionalInterfaceConversionTest.java Fri Sep 01 14:04:20 2017 +0200
@@ -188,11 +188,11 @@
@Override
public void doWork() throws IOException {
- check(newCompilationTask()
+ newCompilationTask()
.withSourceFromTemplate("Sam", samSource)
.withSourceFromTemplate("PackageClass", pkgClassSource)
.withSourceFromTemplate("Client", clientSource, this::importStmt)
- .analyze());
+ .analyze(this::check);
}
ComboParameter importStmt(String name) {
--- a/test/langtools/tools/javac/lambda/LambdaParserTest.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/lambda/LambdaParserTest.java Fri Sep 01 14:04:20 2017 +0200
@@ -246,9 +246,9 @@
@Override
public void doWork() throws IOException {
- check(newCompilationTask()
+ newCompilationTask()
.withSourceFromTemplate(template)
- .parse());
+ .parse(this::check);
}
void check(Result<?> res) {
--- a/test/langtools/tools/javac/lambda/MethodReferenceParserTest.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/lambda/MethodReferenceParserTest.java Fri Sep 01 14:04:20 2017 +0200
@@ -203,9 +203,9 @@
@Override
public void doWork() throws IOException {
- check(newCompilationTask()
+ newCompilationTask()
.withSourceFromTemplate(template)
- .parse());
+ .parse(this::check);
}
void check(Result<?> res) {
--- a/test/langtools/tools/javac/lambda/TestInvokeDynamic.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/lambda/TestInvokeDynamic.java Fri Sep 01 14:04:20 2017 +0200
@@ -252,17 +252,16 @@
@Override
public void doWork() throws IOException {
- ComboTask comboTask = newCompilationTask()
+ newCompilationTask()
.withOption("-g")
- .withSourceFromTemplate(source_template);
-
- JavacTaskImpl ct = (JavacTaskImpl)comboTask.getTask();
- Context context = ct.getContext();
- Symtab syms = Symtab.instance(context);
- Names names = Names.instance(context);
- Types types = Types.instance(context);
- ct.addTaskListener(new Indifier(syms, names, types));
- verifyBytecode(comboTask.generate());
+ .withSourceFromTemplate(source_template)
+ .withListenerFactory(context -> {
+ Symtab syms = Symtab.instance(context);
+ Names names = Names.instance(context);
+ Types types = Types.instance(context);
+ return new Indifier(syms, names, types);
+ })
+ .generate(this::verifyBytecode);
}
void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res) {
--- a/test/langtools/tools/javac/lambda/TestLambdaToMethodStats.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/lambda/TestLambdaToMethodStats.java Fri Sep 01 14:04:20 2017 +0200
@@ -121,10 +121,10 @@
@Override
public void doWork() throws IOException {
- check(newCompilationTask()
+ newCompilationTask()
.withOption("--debug:dumpLambdaToMethodStats")
.withSourceFromTemplate(template)
- .generate());
+ .generate(this::check);
}
void check(Result<?> res) {
--- a/test/langtools/tools/javac/lambda/bytecode/TestLambdaBytecode.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/lambda/bytecode/TestLambdaBytecode.java Fri Sep 01 14:04:20 2017 +0200
@@ -193,9 +193,9 @@
@Override
public void doWork() throws IOException {
- verifyBytecode(newCompilationTask()
+ newCompilationTask()
.withSourceFromTemplate(source_template)
- .generate());
+ .generate(this::verifyBytecode);
}
void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res) {
--- a/test/langtools/tools/javac/lambda/mostSpecific/StructuralMostSpecificTest.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/lambda/mostSpecific/StructuralMostSpecificTest.java Fri Sep 01 14:04:20 2017 +0200
@@ -208,10 +208,10 @@
@Override
public void doWork() throws Throwable {
- check(newCompilationTask()
+ newCompilationTask()
.withSourceFromTemplate(sourceTemplate)
.withOption("--debug:verboseResolution=all,-predef,-internal,-object-init")
- .analyze());
+ .analyze(this::check);
}
void check(Result<Iterable<? extends Element>> result) {
--- a/test/langtools/tools/javac/lambda/typeInference/combo/TypeInferenceComboTest.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/lambda/typeInference/combo/TypeInferenceComboTest.java Fri Sep 01 14:04:20 2017 +0200
@@ -281,14 +281,14 @@
@Override
public void doWork() throws IOException {
- Result<?> res = newCompilationTask()
+ newCompilationTask()
.withSourceFromTemplate("Sam", sam_template, this::samClass)
.withSourceFromTemplate("Client", client_template, this::clientContext)
- .analyze();
-
- if (res.hasErrors() == checkTypeInference()) {
- fail("Unexpected compilation output when compiling instance: " + res.compilationInfo());
- }
+ .analyze(res -> {
+ if (res.hasErrors() == checkTypeInference()) {
+ fail("Unexpected compilation output when compiling instance: " + res.compilationInfo());
+ }
+ });
}
ComboParameter samClass(String parameterName) {
--- a/test/langtools/tools/javac/lib/combo/ComboTask.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/lib/combo/ComboTask.java Fri Sep 01 14:04:20 2017 +0200
@@ -26,8 +26,9 @@
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TaskListener;
-import com.sun.tools.javac.api.JavacTool;
+import com.sun.tools.javac.api.JavacTaskImpl;
import com.sun.tools.javac.util.Assert;
+import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import combo.ComboParameter.Resolver;
@@ -40,16 +41,11 @@
import java.io.IOException;
import java.io.Writer;
import java.net.URI;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ArrayList;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.HashMap;
import java.util.Map;
-import java.util.Optional;
import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
/**
* This class represents a compilation task associated with a combo test instance. This is a small
@@ -73,8 +69,8 @@
/** Listeners associated with this task. */
private List<TaskListener> listeners = List.nil();
- /** Underlying javac task object. */
- private JavacTask task;
+ /** Listener factories associated with this task. */
+ private List<Function<Context, TaskListener>> listenerFactories = List.nil();
/** Combo execution environment. */
private ComboTestHelper<?>.Env env;
@@ -170,77 +166,55 @@
}
/**
+ * Add a task listener factory to this task.
+ */
+ public ComboTask withListenerFactory(Function<Context, TaskListener> factory) {
+ listenerFactories = listenerFactories.prepend(factory);
+ return this;
+ }
+
+ /**
* Parse the sources associated with this task.
*/
- public Result<Iterable<? extends CompilationUnitTree>> parse() throws IOException {
- return new Result<>(getTask().parse());
+ public void parse(Consumer<Result<Iterable<? extends CompilationUnitTree>>> c) {
+ doRunTest(c, JavacTask::parse);
}
/**
* Parse and analyzes the sources associated with this task.
*/
- public Result<Iterable<? extends Element>> analyze() throws IOException {
- return new Result<>(getTask().analyze());
+ public void analyze(Consumer<Result<Iterable<? extends Element>>> c) {
+ doRunTest(c, JavacTask::analyze);
}
/**
* Parse, analyze and perform code generation for the sources associated with this task.
*/
- public Result<Iterable<? extends JavaFileObject>> generate() throws IOException {
- return new Result<>(getTask().generate());
- }
-
- /**
- * Parse, analyze, perform code generation for the sources associated with this task and finally
- * executes them
- */
- public <Z> Optional<Z> execute(Function<ExecutionTask, Z> executionFunc) throws IOException {
- Result<Iterable<? extends JavaFileObject>> generationResult = generate();
- Iterable<? extends JavaFileObject> jfoIterable = generationResult.get();
- if (generationResult.hasErrors()) {
- // we have nothing else to do
- return Optional.empty();
- }
- java.util.List<URL> urlList = new ArrayList<>();
- for (JavaFileObject jfo : jfoIterable) {
- String urlStr = jfo.toUri().toURL().toString();
- urlStr = urlStr.substring(0, urlStr.length() - jfo.getName().length());
- urlList.add(new URL(urlStr));
- }
- return Optional.of(
- executionFunc.apply(
- new ExecutionTask(new URLClassLoader(urlList.toArray(new URL[urlList.size()])))));
+ public void generate(Consumer<Result<Iterable<? extends JavaFileObject>>> c) {
+ doRunTest(c, JavacTask::generate);
}
- /**
- * Fork a new compilation task; if possible the compilation context from previous executions is
- * retained (see comments in ReusableContext as to when it's safe to do so); otherwise a brand
- * new context is created.
- */
- public JavacTask getTask() {
- if (task == null) {
- ReusableContext context = env.context();
- String opts = options == null ? "" :
- StreamSupport.stream(options.spliterator(), false).collect(Collectors.joining());
- context.clear();
- if (!context.polluted && (context.opts == null || context.opts.equals(opts))) {
- //we can reuse former context
- env.info().ctxReusedCount++;
- } else {
- env.info().ctxDroppedCount++;
- //it's not safe to reuse context - create a new one
- context = env.setContext(new ReusableContext());
+ private <V> void doRunTest(Consumer<Result<Iterable<? extends V>>> c,
+ Convertor<V> task2Data) {
+ env.pool().getTask(out, env.fileManager(),
+ diagsCollector, options, null, sources, task -> {
+ try {
+ for (TaskListener l : listeners) {
+ task.addTaskListener(l);
+ }
+ for (Function<Context, TaskListener> f : listenerFactories) {
+ task.addTaskListener(f.apply(((JavacTaskImpl) task).getContext()));
+ }
+ c.accept(new Result<>(task2Data.convert(task)));
+ return null;
+ } catch (IOException ex) {
+ throw new AssertionError(ex);
}
- context.opts = opts;
- JavacTask javacTask = ((JavacTool)env.javaCompiler()).getTask(out, env.fileManager(),
- diagsCollector, options, null, sources, context);
- javacTask.setTaskListener(context);
- for (TaskListener l : listeners) {
- javacTask.addTaskListener(l);
- }
- task = javacTask;
- }
- return task;
+ });
+ }
+
+ interface Convertor<V> {
+ public Iterable<? extends V> convert(JavacTask task) throws IOException;
}
/**
--- a/test/langtools/tools/javac/lib/combo/ComboTestHelper.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/lib/combo/ComboTestHelper.java Fri Sep 01 14:04:20 2017 +0200
@@ -28,6 +28,7 @@
import javax.tools.ToolProvider;
import java.io.IOException;
+import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -38,6 +39,12 @@
import java.util.function.Predicate;
import java.util.function.Supplier;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+
+import com.sun.source.util.JavacTask;
+import com.sun.tools.javac.api.JavacTaskPool;
/**
* An helper class for defining combinatorial (aka "combo" tests). A combo test is made up of one
@@ -93,8 +100,8 @@
/** Shared file manager used across all combo test instances. */
StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
- /** Shared context used across all combo instances. */
- ReusableContext context = new ReusableContext();
+ /** JavacTask pool shared across all combo instances. */
+ JavacTaskPool pool = new JavacTaskPool(1);
/**
* Set failure mode for this combo test.
@@ -248,7 +255,7 @@
} catch (IOException ex) {
throw new AssertionError("Failure when closing down shared file manager; ", ex);
} finally {
- info.dump();
+ info.dump(this);
}
}
@@ -375,19 +382,16 @@
int passCount;
int comboCount;
int skippedCount;
- int ctxReusedCount;
- int ctxDroppedCount;
Optional<String> lastFailure = Optional.empty();
Optional<Throwable> lastError = Optional.empty();
- void dump() {
+ void dump(ComboTestHelper<?> helper) {
System.err.println(String.format("%d total checks executed", comboCount));
System.err.println(String.format("%d successes found", passCount));
System.err.println(String.format("%d failures found", failCount));
System.err.println(String.format("%d errors found", errCount));
System.err.println(String.format("%d skips found", skippedCount));
- System.err.println(String.format("%d contexts shared", ctxReusedCount));
- System.err.println(String.format("%d contexts dropped", ctxDroppedCount));
+ helper.pool.printStatistics(System.err);
}
public boolean hasFailures() {
@@ -400,7 +404,7 @@
}
/**
- * THe execution environment for a given combo test instance. An environment contains the
+ * The execution environment for a given combo test instance. An environment contains the
* bindings for all the dimensions, along with the combo parameter cache (this is non-empty
* only if one or more dimensions are subclasses of the {@code ComboParameter} interface).
*/
@@ -430,12 +434,8 @@
return comp;
}
- ReusableContext context() {
- return context;
- }
-
- ReusableContext setContext(ReusableContext context) {
- return ComboTestHelper.this.context = context;
+ JavacTaskPool pool() {
+ return pool;
}
}
}
--- a/test/langtools/tools/javac/lib/combo/ReusableContext.java Mon Oct 16 18:15:41 2017 +0530
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,229 +0,0 @@
-/*
- * Copyright (c) 2015, 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.
- *
- * 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 combo;
-
-import com.sun.source.tree.ClassTree;
-import com.sun.source.tree.CompilationUnitTree;
-import com.sun.source.util.JavacTask;
-import com.sun.source.util.TaskEvent;
-import com.sun.source.util.TaskEvent.Kind;
-import com.sun.source.util.TaskListener;
-import com.sun.source.util.TreeScanner;
-import com.sun.tools.javac.api.MultiTaskListener;
-import com.sun.tools.javac.code.Kinds;
-import com.sun.tools.javac.code.Symbol;
-import com.sun.tools.javac.code.Symtab;
-import com.sun.tools.javac.code.Type;
-import com.sun.tools.javac.code.Type.ClassType;
-import com.sun.tools.javac.code.TypeTag;
-import com.sun.tools.javac.code.Types;
-import com.sun.tools.javac.comp.Annotate;
-import com.sun.tools.javac.comp.Check;
-import com.sun.tools.javac.comp.CompileStates;
-import com.sun.tools.javac.comp.Enter;
-import com.sun.tools.javac.comp.Modules;
-import com.sun.tools.javac.main.Arguments;
-import com.sun.tools.javac.main.JavaCompiler;
-import com.sun.tools.javac.tree.JCTree.JCClassDecl;
-import com.sun.tools.javac.util.Context;
-import com.sun.tools.javac.util.Log;
-
-import javax.tools.Diagnostic;
-import javax.tools.DiagnosticListener;
-import javax.tools.JavaFileManager;
-import javax.tools.JavaFileObject;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * A reusable context is a context that can be used safely across multiple compilation rounds
- * arising from execution of a combo test. It achieves reuse by replacing some components
- * (most notably JavaCompiler and Log) with reusable counterparts, and by exposing a method
- * to cleanup leftovers from previous compilation.
- * <p>
- * There are, however, situations in which reusing the context is not safe: (i) when different
- * compilations are using different sets of compiler options (as most option values are cached
- * inside components themselves) and (ii) when the compilation unit happens to redefine classes
- * in the java.* packages.
- */
-class ReusableContext extends Context implements TaskListener {
-
- Set<CompilationUnitTree> roots = new HashSet<>();
-
- String opts;
- boolean polluted = false;
-
- ReusableContext() {
- super();
- put(Log.logKey, ReusableLog.factory);
- put(JavaCompiler.compilerKey, ReusableJavaCompiler.factory);
- }
-
- void clear() {
- drop(Arguments.argsKey);
- drop(DiagnosticListener.class);
- drop(Log.outKey);
- drop(Log.errKey);
- drop(JavaFileManager.class);
- drop(JavacTask.class);
-
- if (ht.get(Log.logKey) instanceof ReusableLog) {
- //log already inited - not first round
- ((ReusableLog)Log.instance(this)).clear();
- Enter.instance(this).newRound();
- ((ReusableJavaCompiler)ReusableJavaCompiler.instance(this)).clear();
- Types.instance(this).newRound();
- Check.instance(this).newRound();
- Modules.instance(this).newRound();
- Annotate.instance(this).newRound();
- CompileStates.instance(this).clear();
- MultiTaskListener.instance(this).clear();
-
- //find if any of the roots have redefined java.* classes
- Symtab syms = Symtab.instance(this);
- pollutionScanner.scan(roots, syms);
- roots.clear();
- }
- }
-
- /**
- * This scanner detects as to whether the shared context has been polluted. This happens
- * whenever a compiled program redefines a core class (in 'java.*' package) or when
- * (typically because of cyclic inheritance) the symbol kind of a core class has been touched.
- */
- TreeScanner<Void, Symtab> pollutionScanner = new TreeScanner<Void, Symtab>() {
- @Override
- public Void visitClass(ClassTree node, Symtab syms) {
- Symbol sym = ((JCClassDecl)node).sym;
- if (sym != null) {
- syms.removeClass(sym.packge().modle, sym.flatName());
- Type sup = supertype(sym);
- if (isCoreClass(sym) ||
- (sup != null && isCoreClass(sup.tsym) && sup.tsym.kind != Kinds.Kind.TYP)) {
- polluted = true;
- }
- }
- return super.visitClass(node, syms);
- }
-
- private boolean isCoreClass(Symbol s) {
- return s.flatName().toString().startsWith("java.");
- }
-
- private Type supertype(Symbol s) {
- if (s.type == null ||
- !s.type.hasTag(TypeTag.CLASS)) {
- return null;
- } else {
- ClassType ct = (ClassType)s.type;
- return ct.supertype_field;
- }
- }
- };
-
- @Override
- public void finished(TaskEvent e) {
- if (e.getKind() == Kind.PARSE) {
- roots.add(e.getCompilationUnit());
- }
- }
-
- @Override
- public void started(TaskEvent e) {
- //do nothing
- }
-
- <T> void drop(Key<T> k) {
- ht.remove(k);
- }
-
- <T> void drop(Class<T> c) {
- ht.remove(key(c));
- }
-
- /**
- * Reusable JavaCompiler; exposes a method to clean up the component from leftovers associated with
- * previous compilations.
- */
- static class ReusableJavaCompiler extends JavaCompiler {
-
- static Factory<JavaCompiler> factory = ReusableJavaCompiler::new;
-
- ReusableJavaCompiler(Context context) {
- super(context);
- }
-
- @Override
- public void close() {
- //do nothing
- }
-
- void clear() {
- newRound();
- }
-
- @Override
- protected void checkReusable() {
- //do nothing - it's ok to reuse the compiler
- }
- }
-
- /**
- * Reusable Log; exposes a method to clean up the component from leftovers associated with
- * previous compilations.
- */
- static class ReusableLog extends Log {
-
- static Factory<Log> factory = ReusableLog::new;
-
- Context context;
-
- ReusableLog(Context context) {
- super(context);
- this.context = context;
- }
-
- void clear() {
- recorded.clear();
- sourceMap.clear();
- nerrors = 0;
- nwarnings = 0;
- //Set a fake listener that will lazily lookup the context for the 'real' listener. Since
- //this field is never updated when a new task is created, we cannot simply reset the field
- //or keep old value. This is a hack to workaround the limitations in the current infrastructure.
- diagListener = new DiagnosticListener<JavaFileObject>() {
- DiagnosticListener<JavaFileObject> cachedListener;
-
- @Override
- @SuppressWarnings("unchecked")
- public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
- if (cachedListener == null) {
- cachedListener = context.get(DiagnosticListener.class);
- }
- cachedListener.report(diagnostic);
- }
- };
- }
- }
-}
--- a/test/langtools/tools/javac/multicatch/7030606/DisjunctiveTypeWellFormednessTest.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/multicatch/7030606/DisjunctiveTypeWellFormednessTest.java Fri Sep 01 14:04:20 2017 +0200
@@ -128,9 +128,9 @@
@Override
public void doWork() throws IOException {
- check(newCompilationTask()
+ newCompilationTask()
.withSourceFromTemplate(template)
- .analyze());
+ .analyze(this::check);
}
void check(Result<?> res) {
--- a/test/langtools/tools/javac/resolve/BitWiseOperators.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/resolve/BitWiseOperators.java Fri Sep 01 14:04:20 2017 +0200
@@ -100,13 +100,14 @@
@Override
public void doWork() throws IOException {
- Result<?> res = newCompilationTask()
+ newCompilationTask()
.withSourceFromTemplate(template)
- .analyze();
- if (res.hasErrors() == OperandType.compatible(opTypes[0], opTypes[1])) {
- fail("Unexpected behavior. Type1: " + opTypes[0] +
- "; type2: " + opTypes[1] +
- "; " + res.compilationInfo());
- }
+ .analyze(res -> {
+ if (res.hasErrors() == OperandType.compatible(opTypes[0], opTypes[1])) {
+ fail("Unexpected behavior. Type1: " + opTypes[0] +
+ "; type2: " + opTypes[1] +
+ "; " + res.compilationInfo());
+ }
+ });
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/tree/ScopeClassHeaderTest.java Fri Sep 01 14:04:20 2017 +0200
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8186694
+ * @summary Verify that taking a Scope inside a class header
+ * does not taint internal structures
+ * @modules jdk.compiler
+ * @run main ScopeClassHeaderTest
+ */
+
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.Scope;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TreePathScanner;
+import com.sun.source.util.Trees;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.ToolProvider;
+
+import com.sun.source.tree.IdentifierTree;
+
+public class ScopeClassHeaderTest {
+
+ public static void main(String... args) throws Exception {
+ verifyScopeForClassHeader();
+ }
+
+ private static void verifyScopeForClassHeader() throws Exception {
+ JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
+ JavaFileObject source = new SimpleJavaFileObject(URI.create("mem://Test.java"), Kind.SOURCE) {
+ @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+ return "import java.util.*; class O { public void m() { class X<T extends ArrayList> { public void test() { String o; } } } }";
+ }
+ @Override public boolean isNameCompatible(String simpleName, Kind kind) {
+ return !"module-info".equals(simpleName);
+ }
+ };
+ Iterable<? extends JavaFileObject> fos = Collections.singletonList(source);
+ JavacTask task = (JavacTask) tool.getTask(null, null, null, new ArrayList<String>(), null, fos);
+ final Trees trees = Trees.instance(task);
+ CompilationUnitTree cu = task.parse().iterator().next();
+
+ task.analyze();
+
+ new TreePathScanner<Void, Void>() {
+ @Override
+ public Void visitIdentifier(IdentifierTree node, Void p) {
+ if (node.getName().contentEquals("ArrayList") || node.getName().contentEquals("String")) {
+ Scope scope = trees.getScope(getCurrentPath());
+ System.err.println("scope: " + scope);
+ }
+ return super.visitIdentifier(node, p);
+ }
+ }.scan(cu, null);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/util/JavacTaskPoolTest.java Fri Sep 01 14:04:20 2017 +0200
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ *
+ * 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 8186694
+ * @summary Check that JavacTaskPool reuses JavacTask internals when it should
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ * @run main JavacTaskPoolTest
+ */
+
+import java.util.List;
+
+import javax.lang.model.util.Types;
+
+import com.sun.tools.javac.api.JavacTaskPool;
+
+public class JavacTaskPoolTest {
+ public static void main(String... args) throws Exception {
+ new JavacTaskPoolTest().run();
+ }
+
+ void run() throws Exception {
+ JavacTaskPool pool = new JavacTaskPool(2);
+ Types tps1 = pool.getTask(null, null, null, List.of("-XDone"), null, null, task -> {
+ task.getElements(); //initialize
+ return task.getTypes();
+ });
+ Types tps2 = pool.getTask(null, null, null, List.of("-XDone"), null, null, task -> {
+ task.getElements(); //initialize
+ return task.getTypes();
+ });
+
+ assertSame(tps1, tps2);
+
+ Types tps3 = pool.getTask(null, null, null, List.of("-XDtwo"), null, null, task -> {
+ task.getElements(); //initialize
+ return task.getTypes();
+ });
+
+ assertNotSame(tps1, tps3);
+
+ Types tps4 = pool.getTask(null, null, null, List.of("-XDthree"), null, null, task -> {
+ task.getElements(); //initialize
+ return task.getTypes();
+ });
+
+ assertNotSame(tps1, tps4);
+ assertNotSame(tps3, tps4);
+
+ Types tps5 = pool.getTask(null, null, null, List.of("-XDone"), null, null, task -> {
+ task.getElements(); //initialize
+ return task.getTypes();
+ });
+
+ assertNotSame(tps1, tps5);
+ }
+
+ void assertSame(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new IllegalStateException("expected=" + expected + "; actual=" + actual);
+ }
+ }
+
+ void assertNotSame(Object expected, Object actual) {
+ if (expected == actual) {
+ throw new IllegalStateException("expected=" + expected + "; actual=" + actual);
+ }
+ }
+
+}
--- a/test/langtools/tools/javac/varargs/7042566/T7042566.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/varargs/7042566/T7042566.java Fri Sep 01 14:04:20 2017 +0200
@@ -224,9 +224,9 @@
@Override
public void doWork() throws IOException {
- check(newCompilationTask()
+ newCompilationTask()
.withSourceFromTemplate(source_template, this::getMethodDecl)
- .generate());
+ .generate(this::check);
}
ComboParameter getMethodDecl(String parameterName) {
--- a/test/langtools/tools/javac/varargs/warning/Warn4.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/varargs/warning/Warn4.java Fri Sep 01 14:04:20 2017 +0200
@@ -231,12 +231,12 @@
@Override
public void doWork() throws IOException {
- check(newCompilationTask()
+ newCompilationTask()
.withOption("-Xlint:unchecked")
.withOption("-source")
.withOption(sourceLevel.sourceKey)
.withSourceFromTemplate(template)
- .analyze());
+ .analyze(this::check);
}
void check(Result<?> res) {
--- a/test/langtools/tools/javac/varargs/warning/Warn5.java Mon Oct 16 18:15:41 2017 +0530
+++ b/test/langtools/tools/javac/varargs/warning/Warn5.java Fri Sep 01 14:04:20 2017 +0200
@@ -235,12 +235,12 @@
@Override
public void doWork() throws IOException {
- check(newCompilationTask()
+ newCompilationTask()
.withOption(xlint.getXlintOption())
.withOption("-source")
.withOption(sourceLevel.sourceKey)
.withSourceFromTemplate(template)
- .analyze());
+ .analyze(this::check);
}
void check(Result<?> res) {