8186694: JShell: speed-up compilation by reusing compiler instances
authorjlahoda
Fri, 01 Sep 2017 14:04:20 +0200
changeset 47350 d65c3b21081c
parent 47349 5958eaae167a
child 47351 fff3970bd14f
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
src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java
src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java
src/jdk.jshell/share/classes/jdk/jshell/Eval.java
src/jdk.jshell/share/classes/jdk/jshell/ExpressionToTypeInfo.java
src/jdk.jshell/share/classes/jdk/jshell/ReplParserFactory.java
src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java
src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java
test/langtools/tools/javac/T7093325.java
test/langtools/tools/javac/cast/intersection/IntersectionTypeCastTest.java
test/langtools/tools/javac/defaultMethods/static/hiding/InterfaceMethodHidingTest.java
test/langtools/tools/javac/defaultMethods/super/TestDefaultSuperCall.java
test/langtools/tools/javac/failover/CheckAttributedTree.java
test/langtools/tools/javac/generics/diamond/7046778/DiamondAndInnerClassTest.java
test/langtools/tools/javac/generics/inference/8176534/TestUncheckedCalls.java
test/langtools/tools/javac/generics/rawOverride/7062745/GenericOverrideTest.java
test/langtools/tools/javac/lambda/FunctionalInterfaceConversionTest.java
test/langtools/tools/javac/lambda/LambdaParserTest.java
test/langtools/tools/javac/lambda/MethodReferenceParserTest.java
test/langtools/tools/javac/lambda/TestInvokeDynamic.java
test/langtools/tools/javac/lambda/TestLambdaToMethodStats.java
test/langtools/tools/javac/lambda/bytecode/TestLambdaBytecode.java
test/langtools/tools/javac/lambda/mostSpecific/StructuralMostSpecificTest.java
test/langtools/tools/javac/lambda/typeInference/combo/TypeInferenceComboTest.java
test/langtools/tools/javac/lib/combo/ComboTask.java
test/langtools/tools/javac/lib/combo/ComboTestHelper.java
test/langtools/tools/javac/lib/combo/ReusableContext.java
test/langtools/tools/javac/multicatch/7030606/DisjunctiveTypeWellFormednessTest.java
test/langtools/tools/javac/resolve/BitWiseOperators.java
test/langtools/tools/javac/tree/ScopeClassHeaderTest.java
test/langtools/tools/javac/util/JavacTaskPoolTest.java
test/langtools/tools/javac/varargs/7042566/T7042566.java
test/langtools/tools/javac/varargs/warning/Warn4.java
test/langtools/tools/javac/varargs/warning/Warn5.java
--- /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) {