mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8186694: JShell: speed-up compilation by reusing compiler instances
Generalizing ReusableContext and using it in JShell to speed up processing. Reviewed-by: mcimadamore, rfield
This commit is contained in:
parent
fcf9b5115d
commit
f66b1c7a8b
34 changed files with 1313 additions and 882 deletions
|
@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -241,12 +241,19 @@ public class TypeEnter implements Completer {
|
||||||
boolean firstToComplete = queue.isEmpty();
|
boolean firstToComplete = queue.isEmpty();
|
||||||
|
|
||||||
Phase prevTopLevelPhase = topLevelPhase;
|
Phase prevTopLevelPhase = topLevelPhase;
|
||||||
|
boolean success = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
topLevelPhase = this;
|
topLevelPhase = this;
|
||||||
doCompleteEnvs(envs);
|
doCompleteEnvs(envs);
|
||||||
|
success = true;
|
||||||
} finally {
|
} finally {
|
||||||
topLevelPhase = prevTopLevelPhase;
|
topLevelPhase = prevTopLevelPhase;
|
||||||
|
if (!success && firstToComplete) {
|
||||||
|
//an exception was thrown, e.g. BreakAttr:
|
||||||
|
//the queue would become stale, clear it:
|
||||||
|
queue.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstToComplete) {
|
if (firstToComplete) {
|
||||||
|
|
|
@ -45,6 +45,7 @@ import jdk.jshell.SourceCodeAnalysis.Completeness;
|
||||||
import com.sun.source.tree.Tree;
|
import com.sun.source.tree.Tree;
|
||||||
import static jdk.jshell.CompletenessAnalyzer.TK.*;
|
import static jdk.jshell.CompletenessAnalyzer.TK.*;
|
||||||
import jdk.jshell.TaskFactory.ParseTask;
|
import jdk.jshell.TaskFactory.ParseTask;
|
||||||
|
import jdk.jshell.TaskFactory.Worker;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
@ -85,7 +86,7 @@ class CompletenessAnalyzer {
|
||||||
try {
|
try {
|
||||||
Parser parser = new Parser(
|
Parser parser = new Parser(
|
||||||
() -> new Matched(scannerFactory.newScanner(s, false)),
|
() -> new Matched(scannerFactory.newScanner(s, false)),
|
||||||
() -> proc.taskFactory.parse(s));
|
worker -> proc.taskFactory.parse(s, worker));
|
||||||
Completeness stat = parser.parseUnit();
|
Completeness stat = parser.parseUnit();
|
||||||
int endPos = stat == Completeness.UNKNOWN
|
int endPos = stat == Completeness.UNKNOWN
|
||||||
? s.length()
|
? s.length()
|
||||||
|
@ -561,12 +562,13 @@ class CompletenessAnalyzer {
|
||||||
private static class Parser {
|
private static class Parser {
|
||||||
|
|
||||||
private final Supplier<Matched> matchedFactory;
|
private final Supplier<Matched> matchedFactory;
|
||||||
private final Supplier<ParseTask> parseFactory;
|
private final Function<Worker<ParseTask, Completeness>, Completeness> parseFactory;
|
||||||
private Matched in;
|
private Matched in;
|
||||||
private CT token;
|
private CT token;
|
||||||
private Completeness checkResult;
|
private Completeness checkResult;
|
||||||
|
|
||||||
Parser(Supplier<Matched> matchedFactory, Supplier<ParseTask> parseFactory) {
|
Parser(Supplier<Matched> matchedFactory,
|
||||||
|
Function<Worker<ParseTask, Completeness>, Completeness> parseFactory) {
|
||||||
this.matchedFactory = matchedFactory;
|
this.matchedFactory = matchedFactory;
|
||||||
this.parseFactory = parseFactory;
|
this.parseFactory = parseFactory;
|
||||||
resetInput();
|
resetInput();
|
||||||
|
@ -692,7 +694,7 @@ class CompletenessAnalyzer {
|
||||||
|
|
||||||
public Completeness disambiguateDeclarationVsExpression() {
|
public Completeness disambiguateDeclarationVsExpression() {
|
||||||
// String folding messes up position information.
|
// String folding messes up position information.
|
||||||
ParseTask pt = parseFactory.get();
|
return parseFactory.apply(pt -> {
|
||||||
List<? extends Tree> units = pt.units();
|
List<? extends Tree> units = pt.units();
|
||||||
if (units.isEmpty()) {
|
if (units.isEmpty()) {
|
||||||
return error();
|
return error();
|
||||||
|
@ -716,6 +718,7 @@ class CompletenessAnalyzer {
|
||||||
default:
|
default:
|
||||||
return error();
|
return error();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Completeness parseExpressionStatement() {
|
public Completeness parseExpressionStatement() {
|
||||||
|
|
|
@ -177,7 +177,7 @@ class Eval {
|
||||||
if (compileSource.length() == 0) {
|
if (compileSource.length() == 0) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
ParseTask pt = state.taskFactory.parse(compileSource);
|
return state.taskFactory.parse(compileSource, pt -> {
|
||||||
List<? extends Tree> units = pt.units();
|
List<? extends Tree> units = pt.units();
|
||||||
if (units.isEmpty()) {
|
if (units.isEmpty()) {
|
||||||
return compileFailResult(pt, userSource, Kind.ERRONEOUS);
|
return compileFailResult(pt, userSource, Kind.ERRONEOUS);
|
||||||
|
@ -188,29 +188,30 @@ class Eval {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Erase illegal/ignored modifiers
|
// Erase illegal/ignored modifiers
|
||||||
compileSource = new MaskCommentsAndModifiers(compileSource, true).cleared();
|
String compileSourceInt = new MaskCommentsAndModifiers(compileSource, true).cleared();
|
||||||
|
|
||||||
state.debug(DBG_GEN, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
|
state.debug(DBG_GEN, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
|
||||||
switch (unitTree.getKind()) {
|
switch (unitTree.getKind()) {
|
||||||
case IMPORT:
|
case IMPORT:
|
||||||
return processImport(userSource, compileSource);
|
return processImport(userSource, compileSourceInt);
|
||||||
case VARIABLE:
|
case VARIABLE:
|
||||||
return processVariables(userSource, units, compileSource, pt);
|
return processVariables(userSource, units, compileSourceInt, pt);
|
||||||
case EXPRESSION_STATEMENT:
|
case EXPRESSION_STATEMENT:
|
||||||
return processExpression(userSource, compileSource);
|
return processExpression(userSource, compileSourceInt);
|
||||||
case CLASS:
|
case CLASS:
|
||||||
return processClass(userSource, unitTree, compileSource, SubKind.CLASS_SUBKIND, pt);
|
return processClass(userSource, unitTree, compileSourceInt, SubKind.CLASS_SUBKIND, pt);
|
||||||
case ENUM:
|
case ENUM:
|
||||||
return processClass(userSource, unitTree, compileSource, SubKind.ENUM_SUBKIND, pt);
|
return processClass(userSource, unitTree, compileSourceInt, SubKind.ENUM_SUBKIND, pt);
|
||||||
case ANNOTATION_TYPE:
|
case ANNOTATION_TYPE:
|
||||||
return processClass(userSource, unitTree, compileSource, SubKind.ANNOTATION_TYPE_SUBKIND, pt);
|
return processClass(userSource, unitTree, compileSourceInt, SubKind.ANNOTATION_TYPE_SUBKIND, pt);
|
||||||
case INTERFACE:
|
case INTERFACE:
|
||||||
return processClass(userSource, unitTree, compileSource, SubKind.INTERFACE_SUBKIND, pt);
|
return processClass(userSource, unitTree, compileSourceInt, SubKind.INTERFACE_SUBKIND, pt);
|
||||||
case METHOD:
|
case METHOD:
|
||||||
return processMethod(userSource, unitTree, compileSource, pt);
|
return processMethod(userSource, unitTree, compileSourceInt, pt);
|
||||||
default:
|
default:
|
||||||
return processStatement(userSource, compileSource);
|
return processStatement(userSource, compileSourceInt);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Snippet> processImport(String userSource, String compileSource) {
|
private List<Snippet> processImport(String userSource, String compileSource) {
|
||||||
|
@ -295,9 +296,9 @@ class Eval {
|
||||||
Range rtype = dis.treeToRange(baseType);
|
Range rtype = dis.treeToRange(baseType);
|
||||||
typeWrap = Wrap.rangeWrap(compileSource, rtype);
|
typeWrap = Wrap.rangeWrap(compileSource, rtype);
|
||||||
} else {
|
} else {
|
||||||
AnalyzeTask at = trialCompile(Wrap.methodWrap(compileSource));
|
DiagList dl = trialCompile(Wrap.methodWrap(compileSource));
|
||||||
if (at.hasErrors()) {
|
if (dl.hasErrors()) {
|
||||||
return compileFailResult(at, userSource, kindOfTree(unitTree));
|
return compileFailResult(dl, userSource, kindOfTree(unitTree));
|
||||||
}
|
}
|
||||||
Tree init = vt.getInitializer();
|
Tree init = vt.getInitializer();
|
||||||
if (init != null) {
|
if (init != null) {
|
||||||
|
@ -459,13 +460,13 @@ class Eval {
|
||||||
guts = Wrap.methodWrap(compileSource);
|
guts = Wrap.methodWrap(compileSource);
|
||||||
if (ei == null) {
|
if (ei == null) {
|
||||||
// We got no type info, check for not a statement by trying
|
// We got no type info, check for not a statement by trying
|
||||||
AnalyzeTask at = trialCompile(guts);
|
DiagList dl = trialCompile(guts);
|
||||||
if (at.getDiagnostics().hasNotStatement()) {
|
if (dl.hasNotStatement()) {
|
||||||
guts = Wrap.methodReturnWrap(compileSource);
|
guts = Wrap.methodReturnWrap(compileSource);
|
||||||
at = trialCompile(guts);
|
dl = trialCompile(guts);
|
||||||
}
|
}
|
||||||
if (at.hasErrors()) {
|
if (dl.hasErrors()) {
|
||||||
return compileFailResult(at, userSource, Kind.EXPRESSION);
|
return compileFailResult(dl, userSource, Kind.EXPRESSION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
|
snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
|
||||||
|
@ -496,32 +497,32 @@ class Eval {
|
||||||
private List<Snippet> processStatement(String userSource, String compileSource) {
|
private List<Snippet> processStatement(String userSource, String compileSource) {
|
||||||
Wrap guts = Wrap.methodWrap(compileSource);
|
Wrap guts = Wrap.methodWrap(compileSource);
|
||||||
// Check for unreachable by trying
|
// Check for unreachable by trying
|
||||||
AnalyzeTask at = trialCompile(guts);
|
DiagList dl = trialCompile(guts);
|
||||||
if (at.hasErrors()) {
|
if (dl.hasErrors()) {
|
||||||
if (at.getDiagnostics().hasUnreachableError()) {
|
if (dl.hasUnreachableError()) {
|
||||||
guts = Wrap.methodUnreachableSemiWrap(compileSource);
|
guts = Wrap.methodUnreachableSemiWrap(compileSource);
|
||||||
at = trialCompile(guts);
|
dl = trialCompile(guts);
|
||||||
if (at.hasErrors()) {
|
if (dl.hasErrors()) {
|
||||||
if (at.getDiagnostics().hasUnreachableError()) {
|
if (dl.hasUnreachableError()) {
|
||||||
// Without ending semicolon
|
// Without ending semicolon
|
||||||
guts = Wrap.methodUnreachableWrap(compileSource);
|
guts = Wrap.methodUnreachableWrap(compileSource);
|
||||||
at = trialCompile(guts);
|
dl = trialCompile(guts);
|
||||||
}
|
}
|
||||||
if (at.hasErrors()) {
|
if (dl.hasErrors()) {
|
||||||
return compileFailResult(at, userSource, Kind.STATEMENT);
|
return compileFailResult(dl, userSource, Kind.STATEMENT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return compileFailResult(at, userSource, Kind.STATEMENT);
|
return compileFailResult(dl, userSource, Kind.STATEMENT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Snippet snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
|
Snippet snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
|
||||||
return singletonList(snip);
|
return singletonList(snip);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AnalyzeTask trialCompile(Wrap guts) {
|
private DiagList trialCompile(Wrap guts) {
|
||||||
OuterWrap outer = state.outerMap.wrapInTrialClass(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) {
|
private List<Snippet> processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt) {
|
||||||
|
@ -751,19 +752,22 @@ class Eval {
|
||||||
|
|
||||||
ins.stream().forEach(Unit::initialize);
|
ins.stream().forEach(Unit::initialize);
|
||||||
ins.stream().forEach(u -> u.setWrap(ins, ins));
|
ins.stream().forEach(u -> u.setWrap(ins, ins));
|
||||||
AnalyzeTask at = state.taskFactory.new AnalyzeTask(outerWrapSet(ins));
|
state.taskFactory.analyze(outerWrapSet(ins), at -> {
|
||||||
ins.stream().forEach(u -> u.setDiagnostics(at));
|
ins.stream().forEach(u -> u.setDiagnostics(at));
|
||||||
|
|
||||||
// corral any Snippets that need it
|
// corral any Snippets that need it
|
||||||
AnalyzeTask cat;
|
|
||||||
if (ins.stream().anyMatch(u -> u.corralIfNeeded(ins))) {
|
if (ins.stream().anyMatch(u -> u.corralIfNeeded(ins))) {
|
||||||
// if any were corralled, re-analyze everything
|
// if any were corralled, re-analyze everything
|
||||||
cat = state.taskFactory.new AnalyzeTask(outerWrapSet(ins));
|
state.taskFactory.analyze(outerWrapSet(ins), cat -> {
|
||||||
ins.stream().forEach(u -> u.setCorralledDiagnostics(cat));
|
ins.stream().forEach(u -> u.setCorralledDiagnostics(cat));
|
||||||
} else {
|
|
||||||
cat = at;
|
|
||||||
}
|
|
||||||
ins.stream().forEach(u -> u.setStatus(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
|
// compile and load the legit snippets
|
||||||
boolean success;
|
boolean success;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -780,14 +784,14 @@ class Eval {
|
||||||
legit.stream().forEach(u -> u.setWrap(ins, legit));
|
legit.stream().forEach(u -> u.setWrap(ins, legit));
|
||||||
|
|
||||||
// generate class files for those capable
|
// generate class files for those capable
|
||||||
CompileTask ct = state.taskFactory.new CompileTask(outerWrapSet(legit));
|
Result res = state.taskFactory.compile(outerWrapSet(legit), ct -> {
|
||||||
if (!ct.compile()) {
|
if (!ct.compile()) {
|
||||||
// oy! compile failed because of recursive new unresolved
|
// oy! compile failed because of recursive new unresolved
|
||||||
if (legit.stream()
|
if (legit.stream()
|
||||||
.filter(u -> u.smashingErrorDiagnostics(ct))
|
.filter(u -> u.smashingErrorDiagnostics(ct))
|
||||||
.count() > 0) {
|
.count() > 0) {
|
||||||
// try again, with the erroreous removed
|
// try again, with the erroreous removed
|
||||||
continue;
|
return Result.CONTINUE;
|
||||||
} else {
|
} else {
|
||||||
state.debug(DBG_GEN, "Should never happen error-less failure - %s\n",
|
state.debug(DBG_GEN, "Should never happen error-less failure - %s\n",
|
||||||
legit);
|
legit);
|
||||||
|
@ -810,7 +814,15 @@ class Eval {
|
||||||
replaced.stream().forEach(Unit::markForReplacement);
|
replaced.stream().forEach(Unit::markForReplacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
success = toReplace.isEmpty();
|
return toReplace.isEmpty() ? Result.SUCESS : Result.FAILURE;
|
||||||
|
});
|
||||||
|
|
||||||
|
switch (res) {
|
||||||
|
case CONTINUE: continue;
|
||||||
|
case SUCESS: success = true; break;
|
||||||
|
default:
|
||||||
|
case FAILURE: success = false; break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -830,6 +842,8 @@ class Eval {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//where:
|
||||||
|
enum Result {SUCESS, FAILURE, CONTINUE}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If there are classes to load, loads by calling the execution engine.
|
* If there are classes to load, loads by calling the execution engine.
|
||||||
|
@ -893,6 +907,8 @@ class Eval {
|
||||||
|
|
||||||
final boolean fatal;
|
final boolean fatal;
|
||||||
final String message;
|
final String message;
|
||||||
|
long start;
|
||||||
|
long end;
|
||||||
|
|
||||||
ModifierDiagnostic(List<Modifier> list, boolean fatal) {
|
ModifierDiagnostic(List<Modifier> list, boolean fatal) {
|
||||||
this.fatal = fatal;
|
this.fatal = fatal;
|
||||||
|
@ -910,6 +926,8 @@ class Eval {
|
||||||
? "jshell.diag.modifier.single.fatal"
|
? "jshell.diag.modifier.single.fatal"
|
||||||
: "jshell.diag.modifier.single.ignore";
|
: "jshell.diag.modifier.single.ignore";
|
||||||
this.message = state.messageFormat(key, sb.toString());
|
this.message = state.messageFormat(key, sb.toString());
|
||||||
|
start = dis.getStartPosition(modtree);
|
||||||
|
end = dis.getEndPosition(modtree);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -919,17 +937,17 @@ class Eval {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getPosition() {
|
public long getPosition() {
|
||||||
return dis.getStartPosition(modtree);
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getStartPosition() {
|
public long getStartPosition() {
|
||||||
return dis.getStartPosition(modtree);
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getEndPosition() {
|
public long getEndPosition() {
|
||||||
return dis.getEndPosition(modtree);
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -163,14 +163,15 @@ class ExpressionToTypeInfo {
|
||||||
if (code == null || code.isEmpty()) {
|
if (code == null || code.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
OuterWrap codeWrap = state.outerMap.wrapInTrialClass(Wrap.methodReturnWrap(code));
|
OuterWrap codeWrap = state.outerMap.wrapInTrialClass(Wrap.methodReturnWrap(code));
|
||||||
AnalyzeTask at = state.taskFactory.new AnalyzeTask(codeWrap);
|
try {
|
||||||
|
return state.taskFactory.analyze(codeWrap, at -> {
|
||||||
CompilationUnitTree cu = at.firstCuTree();
|
CompilationUnitTree cu = at.firstCuTree();
|
||||||
if (at.hasErrors() || cu == null) {
|
if (at.hasErrors() || cu == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new ExpressionToTypeInfo(at, cu, state).typeOfExpression();
|
return new ExpressionToTypeInfo(at, cu, state).typeOfExpression();
|
||||||
|
});
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -189,12 +190,13 @@ class ExpressionToTypeInfo {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
OuterWrap codeWrap = state.outerMap.wrapInTrialClass(Wrap.methodWrap("var $$$ = " + code));
|
OuterWrap codeWrap = state.outerMap.wrapInTrialClass(Wrap.methodWrap("var $$$ = " + code));
|
||||||
AnalyzeTask at = state.taskFactory.new AnalyzeTask(codeWrap);
|
return state.taskFactory.analyze(codeWrap, at -> {
|
||||||
CompilationUnitTree cu = at.firstCuTree();
|
CompilationUnitTree cu = at.firstCuTree();
|
||||||
if (at.hasErrors() || cu == null) {
|
if (at.hasErrors() || cu == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new ExpressionToTypeInfo(at, cu, state).typeOfExpression();
|
return new ExpressionToTypeInfo(at, cu, state).typeOfExpression();
|
||||||
|
});
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,8 +40,12 @@ class ReplParserFactory extends ParserFactory {
|
||||||
private final boolean forceExpression;
|
private final boolean forceExpression;
|
||||||
|
|
||||||
public static void preRegister(Context context, boolean forceExpression) {
|
public static void preRegister(Context context, boolean forceExpression) {
|
||||||
|
class Mark {}
|
||||||
|
if (context.get(Mark.class) == null) { //don't register the factory if Context is reused
|
||||||
context.put(parserFactoryKey, (Context.Factory<ParserFactory>)
|
context.put(parserFactoryKey, (Context.Factory<ParserFactory>)
|
||||||
(c -> new ReplParserFactory(c, forceExpression)));
|
(c -> new ReplParserFactory(c, forceExpression)));
|
||||||
|
context.put(Mark.class, new Mark());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ScannerFactory scannerFactory;
|
private final ScannerFactory scannerFactory;
|
||||||
|
|
|
@ -236,7 +236,7 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tree.Kind guessKind(String code) {
|
private Tree.Kind guessKind(String code) {
|
||||||
ParseTask pt = proc.taskFactory.parse(code);
|
return proc.taskFactory.parse(code, pt -> {
|
||||||
List<? extends Tree> units = pt.units();
|
List<? extends Tree> units = pt.units();
|
||||||
if (units.isEmpty()) {
|
if (units.isEmpty()) {
|
||||||
return Tree.Kind.BLOCK;
|
return Tree.Kind.BLOCK;
|
||||||
|
@ -244,6 +244,7 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
|
||||||
Tree unitTree = units.get(0);
|
Tree unitTree = units.get(0);
|
||||||
proc.debug(DBG_COMPA, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
|
proc.debug(DBG_COMPA, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
|
||||||
return unitTree.getKind();
|
return unitTree.getKind();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: would be better handled through a lexer:
|
//TODO: would be better handled through a lexer:
|
||||||
|
@ -295,7 +296,7 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Suggestion> computeSuggestions(OuterWrap code, int cursor, int[] anchor) {
|
private List<Suggestion> computeSuggestions(OuterWrap code, int cursor, int[] anchor) {
|
||||||
AnalyzeTask at = proc.taskFactory.new AnalyzeTask(code);
|
return proc.taskFactory.analyze(code, at -> {
|
||||||
SourcePositions sp = at.trees().getSourcePositions();
|
SourcePositions sp = at.trees().getSourcePositions();
|
||||||
CompilationUnitTree topLevel = at.firstCuTree();
|
CompilationUnitTree topLevel = at.firstCuTree();
|
||||||
List<Suggestion> result = new ArrayList<>();
|
List<Suggestion> result = new ArrayList<>();
|
||||||
|
@ -471,6 +472,7 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
|
||||||
}
|
}
|
||||||
anchor[0] = cursor;
|
anchor[0] = cursor;
|
||||||
return result;
|
return result;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Set<Kind> CLASS_KINDS = EnumSet.of(
|
private static final Set<Kind> CLASS_KINDS = EnumSet.of(
|
||||||
|
@ -1167,7 +1169,7 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|
||||||
OuterWrap codeWrap = proc.outerMap.wrapInTrialClass(Wrap.methodWrap(code));
|
OuterWrap codeWrap = proc.outerMap.wrapInTrialClass(Wrap.methodWrap(code));
|
||||||
AnalyzeTask at = proc.taskFactory.new AnalyzeTask(codeWrap, keepParameterNames);
|
return proc.taskFactory.analyze(codeWrap, List.of(keepParameterNames), at -> {
|
||||||
SourcePositions sp = at.trees().getSourcePositions();
|
SourcePositions sp = at.trees().getSourcePositions();
|
||||||
CompilationUnitTree topLevel = at.firstCuTree();
|
CompilationUnitTree topLevel = at.firstCuTree();
|
||||||
TreePath tp = pathFor(topLevel, sp, codeWrap.snippetIndexToWrapIndex(cursor));
|
TreePath tp = pathFor(topLevel, sp, codeWrap.snippetIndexToWrapIndex(cursor));
|
||||||
|
@ -1239,6 +1241,7 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Documentation constructDocumentation(AnalyzeTask at, JavadocHelper helper, Element el, boolean computeJavadoc) {
|
private Documentation constructDocumentation(AnalyzeTask at, JavadocHelper helper, Element el, boolean computeJavadoc) {
|
||||||
|
@ -1494,25 +1497,25 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public QualifiedNames listQualifiedNames(String code, int cursor) {
|
public QualifiedNames listQualifiedNames(String code, int cursor) {
|
||||||
code = code.substring(0, cursor);
|
String codeFin = code.substring(0, cursor);
|
||||||
if (code.trim().isEmpty()) {
|
if (codeFin.trim().isEmpty()) {
|
||||||
return new QualifiedNames(Collections.emptyList(), -1, true, false);
|
return new QualifiedNames(Collections.emptyList(), -1, true, false);
|
||||||
}
|
}
|
||||||
OuterWrap codeWrap;
|
OuterWrap codeWrap;
|
||||||
switch (guessKind(code)) {
|
switch (guessKind(codeFin)) {
|
||||||
case IMPORT:
|
case IMPORT:
|
||||||
return new QualifiedNames(Collections.emptyList(), -1, true, false);
|
return new QualifiedNames(Collections.emptyList(), -1, true, false);
|
||||||
case METHOD:
|
case METHOD:
|
||||||
codeWrap = proc.outerMap.wrapInTrialClass(Wrap.classMemberWrap(code));
|
codeWrap = proc.outerMap.wrapInTrialClass(Wrap.classMemberWrap(codeFin));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
codeWrap = proc.outerMap.wrapInTrialClass(Wrap.methodWrap(code));
|
codeWrap = proc.outerMap.wrapInTrialClass(Wrap.methodWrap(codeFin));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
AnalyzeTask at = proc.taskFactory.new AnalyzeTask(codeWrap);
|
return proc.taskFactory.analyze(codeWrap, at -> {
|
||||||
SourcePositions sp = at.trees().getSourcePositions();
|
SourcePositions sp = at.trees().getSourcePositions();
|
||||||
CompilationUnitTree topLevel = at.firstCuTree();
|
CompilationUnitTree topLevel = at.firstCuTree();
|
||||||
TreePath tp = pathFor(topLevel, sp, codeWrap.snippetIndexToWrapIndex(code.length()));
|
TreePath tp = pathFor(topLevel, sp, codeWrap.snippetIndexToWrapIndex(codeFin.length()));
|
||||||
if (tp.getLeaf().getKind() != Kind.IDENTIFIER) {
|
if (tp.getLeaf().getKind() != Kind.IDENTIFIER) {
|
||||||
return new QualifiedNames(Collections.emptyList(), -1, true, false);
|
return new QualifiedNames(Collections.emptyList(), -1, true, false);
|
||||||
}
|
}
|
||||||
|
@ -1539,6 +1542,7 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
private boolean isAccessible(AnalyzeTask at, Scope scope, String fqn) {
|
||||||
|
|
|
@ -29,10 +29,8 @@ import com.sun.source.tree.CompilationUnitTree;
|
||||||
import com.sun.source.tree.Tree;
|
import com.sun.source.tree.Tree;
|
||||||
import com.sun.source.util.Trees;
|
import com.sun.source.util.Trees;
|
||||||
import com.sun.tools.javac.api.JavacTaskImpl;
|
import com.sun.tools.javac.api.JavacTaskImpl;
|
||||||
import com.sun.tools.javac.api.JavacTool;
|
|
||||||
import com.sun.tools.javac.util.Context;
|
import com.sun.tools.javac.util.Context;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.tools.Diagnostic;
|
import javax.tools.Diagnostic;
|
||||||
import javax.tools.DiagnosticCollector;
|
import javax.tools.DiagnosticCollector;
|
||||||
|
@ -62,18 +60,28 @@ import javax.tools.FileObject;
|
||||||
import jdk.jshell.MemoryFileManager.SourceMemoryJavaFileObject;
|
import jdk.jshell.MemoryFileManager.SourceMemoryJavaFileObject;
|
||||||
import java.lang.Runtime.Version;
|
import java.lang.Runtime.Version;
|
||||||
import java.nio.CharBuffer;
|
import java.nio.CharBuffer;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
import com.sun.source.tree.Tree.Kind;
|
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.Kinds;
|
||||||
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
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.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.Parser;
|
||||||
|
import com.sun.tools.javac.parser.ParserFactory;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
|
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
|
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCExpression;
|
import com.sun.tools.javac.tree.JCTree.JCExpression;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCTypeCast;
|
import com.sun.tools.javac.tree.JCTree.JCTypeCast;
|
||||||
import com.sun.tools.javac.tree.JCTree.Tag;
|
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.Log.DiscardDiagnosticHandler;
|
||||||
|
import com.sun.tools.javac.util.Names;
|
||||||
import jdk.jshell.Snippet.Status;
|
import jdk.jshell.Snippet.Status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -101,6 +109,7 @@ class TaskFactory {
|
||||||
}
|
}
|
||||||
this.fileManager = new MemoryFileManager(
|
this.fileManager = new MemoryFileManager(
|
||||||
compiler.getStandardFileManager(null, null, null), state);
|
compiler.getStandardFileManager(null, null, null), state);
|
||||||
|
initTaskPool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void addToClasspath(String path) {
|
void addToClasspath(String path) {
|
||||||
|
@ -108,27 +117,130 @@ class TaskFactory {
|
||||||
List<String> args = new ArrayList<>();
|
List<String> args = new ArrayList<>();
|
||||||
args.add(classpath);
|
args.add(classpath);
|
||||||
fileManager().handleOption("-classpath", args.iterator());
|
fileManager().handleOption("-classpath", args.iterator());
|
||||||
|
initTaskPool();
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryFileManager fileManager() {
|
MemoryFileManager fileManager() {
|
||||||
return 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
|
// Parse a snippet and return our parse task handler
|
||||||
ParseTask parse(final String source) {
|
<Z> Z parse(final String source, Worker<ParseTask, Z> worker) {
|
||||||
ParseTask pt = state.taskFactory.new ParseTask(source, false);
|
return parse(source, false, pt -> {
|
||||||
if (!pt.units().isEmpty()
|
if (!pt.units().isEmpty()
|
||||||
&& pt.units().get(0).getKind() == Kind.EXPRESSION_STATEMENT
|
&& pt.units().get(0).getKind() == Kind.EXPRESSION_STATEMENT
|
||||||
&& pt.getDiagnostics().hasOtherThanNotStatementErrors()) {
|
&& pt.getDiagnostics().hasOtherThanNotStatementErrors()) {
|
||||||
// It failed, it may be an expression being incorrectly
|
// It failed, it may be an expression being incorrectly
|
||||||
// parsed as having a leading type variable, example: a < b
|
// parsed as having a leading type variable, example: a < b
|
||||||
// Try forcing interpretation as an expression
|
// Try forcing interpretation as an expression
|
||||||
ParseTask ept = state.taskFactory.new ParseTask(source, true);
|
return parse(source, true, ept -> {
|
||||||
if (!ept.getDiagnostics().hasOtherThanNotStatementErrors()) {
|
if (!ept.getDiagnostics().hasOtherThanNotStatementErrors()) {
|
||||||
return ept;
|
return worker.withTask(ept);
|
||||||
|
} else {
|
||||||
|
return worker.withTask(pt);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return pt;
|
return worker.withTask(pt);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface SourceHandler<T> {
|
private interface SourceHandler<T> {
|
||||||
|
@ -210,11 +322,12 @@ class TaskFactory {
|
||||||
private final Iterable<? extends CompilationUnitTree> cuts;
|
private final Iterable<? extends CompilationUnitTree> cuts;
|
||||||
private final List<? extends Tree> units;
|
private final List<? extends Tree> units;
|
||||||
|
|
||||||
ParseTask(final String source, final boolean forceExpression) {
|
private ParseTask(SourceHandler<String> sh,
|
||||||
super(Stream.of(source),
|
JavacTaskImpl task,
|
||||||
new StringSourceHandler(),
|
DiagnosticCollector<JavaFileObject> diagnostics,
|
||||||
"-XDallowStringFolding=false", "-proc:none");
|
boolean forceExpression) {
|
||||||
ReplParserFactory.preRegister(getContext(), forceExpression);
|
super(sh, task, diagnostics);
|
||||||
|
ReplParserFactory.preRegister(context, forceExpression);
|
||||||
cuts = parse();
|
cuts = parse();
|
||||||
units = Util.stream(cuts)
|
units = Util.stream(cuts)
|
||||||
.flatMap(cut -> {
|
.flatMap(cut -> {
|
||||||
|
@ -249,22 +362,10 @@ class TaskFactory {
|
||||||
|
|
||||||
private final Iterable<? extends CompilationUnitTree> cuts;
|
private final Iterable<? extends CompilationUnitTree> cuts;
|
||||||
|
|
||||||
AnalyzeTask(final OuterWrap wrap, String... extraArgs) {
|
private AnalyzeTask(SourceHandler<OuterWrap> sh,
|
||||||
this(Collections.singletonList(wrap), extraArgs);
|
JavacTaskImpl task,
|
||||||
}
|
DiagnosticCollector<JavaFileObject> diagnostics) {
|
||||||
|
super(sh, task, diagnostics);
|
||||||
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);
|
|
||||||
cuts = analyze();
|
cuts = analyze();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,9 +400,10 @@ class TaskFactory {
|
||||||
|
|
||||||
private final Map<OuterWrap, List<OutputMemoryJavaFileObject>> classObjs = new HashMap<>();
|
private final Map<OuterWrap, List<OutputMemoryJavaFileObject>> classObjs = new HashMap<>();
|
||||||
|
|
||||||
CompileTask(final Collection<OuterWrap> wraps) {
|
CompileTask(SourceHandler<OuterWrap>sh,
|
||||||
super(wraps.stream(), new WrapSourceHandler(),
|
JavacTaskImpl jti,
|
||||||
"-Xlint:unchecked", "-proc:none", "-parameters");
|
DiagnosticCollector<JavaFileObject> diagnostics) {
|
||||||
|
super(sh, jti, diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean compile() {
|
boolean compile() {
|
||||||
|
@ -346,32 +448,30 @@ class TaskFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JavacTaskPool javacTaskPool;
|
||||||
|
|
||||||
|
private void initTaskPool() {
|
||||||
|
javacTaskPool = new JavacTaskPool(5);
|
||||||
|
}
|
||||||
|
|
||||||
abstract class BaseTask {
|
abstract class BaseTask {
|
||||||
|
|
||||||
final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
|
final DiagnosticCollector<JavaFileObject> diagnostics;
|
||||||
final JavacTaskImpl task;
|
final JavacTaskImpl task;
|
||||||
private DiagList diags = null;
|
private DiagList diags = null;
|
||||||
private final SourceHandler<?> sourceHandler;
|
private final SourceHandler<?> sourceHandler;
|
||||||
final Context context = new Context();
|
final Context context;
|
||||||
private Types types;
|
private Types types;
|
||||||
private JavacMessages messages;
|
private JavacMessages messages;
|
||||||
private Trees trees;
|
private Trees trees;
|
||||||
|
|
||||||
private <T>BaseTask(Stream<T> inputs,
|
private <T>BaseTask(SourceHandler<T> sh,
|
||||||
//BiFunction<MemoryFileManager, T, JavaFileObject> sfoCreator,
|
JavacTaskImpl task,
|
||||||
SourceHandler<T> sh,
|
DiagnosticCollector<JavaFileObject> diagnostics) {
|
||||||
String... extraOptions) {
|
|
||||||
this.sourceHandler = sh;
|
this.sourceHandler = sh;
|
||||||
List<String> options = new ArrayList<>(extraOptions.length + state.extraCompilerOptions.size());
|
this.task = task;
|
||||||
options.addAll(Arrays.asList(extraOptions));
|
context = task.getContext();
|
||||||
options.addAll(state.extraCompilerOptions);
|
this.diagnostics = diagnostics;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract Iterable<? extends CompilationUnitTree> cuTrees();
|
abstract Iterable<? extends CompilationUnitTree> cuTrees();
|
||||||
|
@ -478,32 +578,36 @@ class TaskFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
private final JShell state;
|
||||||
|
|
||||||
public JShellJavaCompiler(Context context, JShell state) {
|
public TaskListenerImpl(Context context, JShell state) {
|
||||||
super(context);
|
this.context = context;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processAnnotations(com.sun.tools.javac.util.List<JCCompilationUnit> roots, Collection<String> classnames) {
|
public void finished(TaskEvent e) {
|
||||||
super.processAnnotations(roots, classnames);
|
if (e.getKind() != TaskEvent.Kind.ENTER)
|
||||||
|
return ;
|
||||||
state.maps
|
state.maps
|
||||||
.snippetList()
|
.snippetList()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(s -> s.status() == Status.VALID)
|
.filter(s -> s.status() == Status.VALID)
|
||||||
.filter(s -> s.kind() == Snippet.Kind.VAR)
|
.filter(s -> s.kind() == Snippet.Kind.VAR)
|
||||||
.filter(s -> s.subKind() == Snippet.SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND)
|
.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()));
|
ClassSymbol clazz = syms.getClass(syms.unnamedModule, names.fromString(s.classFullName()));
|
||||||
if (clazz == null || !clazz.isCompleted())
|
if (clazz == null || !clazz.isCompleted())
|
||||||
return;
|
return;
|
||||||
|
@ -520,7 +624,7 @@ class TaskFactory {
|
||||||
JCTypeCast tree = (JCTypeCast) expr;
|
JCTypeCast tree = (JCTypeCast) expr;
|
||||||
if (tree.clazz.hasTag(Tag.TYPEINTERSECTION)) {
|
if (tree.clazz.hasTag(Tag.TYPEINTERSECTION)) {
|
||||||
field.type = attr.attribType(tree.clazz,
|
field.type = attr.attribType(tree.clazz,
|
||||||
((JCClassDecl) roots.head.getTypeDecls().head).sym);
|
((JCClassDecl) root.getTypeDecls().head).sym);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -130,9 +130,9 @@ public class T7093325 extends ComboInstance<T7093325> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
verifyBytecode(newCompilationTask()
|
newCompilationTask()
|
||||||
.withSourceFromTemplate(source_template)
|
.withSourceFromTemplate(source_template)
|
||||||
.generate());
|
.generate(this::verifyBytecode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void verifyBytecode(Result<Iterable<? extends JavaFileObject>> result) {
|
void verifyBytecode(Result<Iterable<? extends JavaFileObject>> result) {
|
||||||
|
|
|
@ -254,9 +254,9 @@ public class IntersectionTypeCastTest extends ComboInstance<IntersectionTypeCast
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
check(newCompilationTask()
|
newCompilationTask()
|
||||||
.withSourceFromTemplate(bodyTemplate)
|
.withSourceFromTemplate(bodyTemplate)
|
||||||
.analyze());
|
.analyze(this::check);
|
||||||
}
|
}
|
||||||
|
|
||||||
String bodyTemplate = "class Test {\n" +
|
String bodyTemplate = "class Test {\n" +
|
||||||
|
|
|
@ -154,10 +154,10 @@ public class InterfaceMethodHidingTest extends ComboInstance<InterfaceMethodHidi
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
check(newCompilationTask()
|
newCompilationTask()
|
||||||
.withOption("-XDallowStaticInterfaceMethods")
|
.withOption("-XDallowStaticInterfaceMethods")
|
||||||
.withSourceFromTemplate(template, this::returnExpr)
|
.withSourceFromTemplate(template, this::returnExpr)
|
||||||
.analyze());
|
.analyze(this::check);
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboParameter returnExpr(String name) {
|
ComboParameter returnExpr(String name) {
|
||||||
|
|
|
@ -303,9 +303,9 @@ public class TestDefaultSuperCall extends ComboInstance<TestDefaultSuperCall> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
check(newCompilationTask()
|
newCompilationTask()
|
||||||
.withSourceFromTemplate(template, this::methodName)
|
.withSourceFromTemplate(template, this::methodName)
|
||||||
.analyze());
|
.analyze(this::check);
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboParameter methodName(String parameterName) {
|
ComboParameter methodName(String parameterName) {
|
||||||
|
|
|
@ -67,6 +67,7 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
import javax.lang.model.element.Element;
|
import javax.lang.model.element.Element;
|
||||||
import javax.swing.DefaultComboBoxModel;
|
import javax.swing.DefaultComboBoxModel;
|
||||||
|
@ -287,29 +288,26 @@ public class CheckAttributedTree {
|
||||||
errWriter.println(file);
|
errWriter.println(file);
|
||||||
fileCount.incrementAndGet();
|
fileCount.incrementAndGet();
|
||||||
NPETester p = new NPETester();
|
NPETester p = new NPETester();
|
||||||
p.test(read(file));
|
readAndCheck(file, p::test);
|
||||||
} catch (AttributionException e) {
|
} catch (Throwable t) {
|
||||||
if (!quiet) {
|
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
|
* @param file the file to be read
|
||||||
* @return the tree for the content of the file
|
* @return the tree for the content of the file
|
||||||
* @throws IOException if any IO errors occur
|
* @throws IOException if any IO errors occur
|
||||||
* @throws AttributionException if any errors occur while analyzing the file
|
* @throws AttributionException if any errors occur while analyzing the file
|
||||||
*/
|
*/
|
||||||
List<Pair<JCCompilationUnit, JCTree>> read(File file) throws IOException, AttributionException {
|
void readAndCheck(File file, BiConsumer<JCCompilationUnit, JCTree> c) throws IOException {
|
||||||
try {
|
|
||||||
Iterable<? extends JavaFileObject> files = fileManager().getJavaFileObjects(file);
|
Iterable<? extends JavaFileObject> files = fileManager().getJavaFileObjects(file);
|
||||||
final List<Element> analyzedElems = new ArrayList<>();
|
final List<Element> analyzedElems = new ArrayList<>();
|
||||||
final List<CompilationUnitTree> trees = new ArrayList<>();
|
final List<CompilationUnitTree> trees = new ArrayList<>();
|
||||||
Iterable<? extends Element> elems = newCompilationTask()
|
newCompilationTask()
|
||||||
.withWriter(pw)
|
.withWriter(pw)
|
||||||
.withOption("--should-stop:at=ATTR")
|
.withOption("--should-stop:at=ATTR")
|
||||||
.withOption("-XDverboseCompilePolicy")
|
.withOption("-XDverboseCompilePolicy")
|
||||||
|
@ -324,24 +322,20 @@ public class CheckAttributedTree {
|
||||||
if (e.getKind() == Kind.PARSE)
|
if (e.getKind() == Kind.PARSE)
|
||||||
trees.add(e.getCompilationUnit());
|
trees.add(e.getCompilationUnit());
|
||||||
}
|
}
|
||||||
}).analyze().get();
|
}).analyze(res -> {
|
||||||
|
Iterable<? extends Element> elems = res.get();
|
||||||
if (!elems.iterator().hasNext())
|
if (!elems.iterator().hasNext())
|
||||||
throw new AttributionException("No results from analyze");
|
throw new AssertionError("No results from analyze");
|
||||||
List<Pair<JCCompilationUnit, JCTree>> res = new ArrayList<>();
|
|
||||||
for (CompilationUnitTree t : trees) {
|
for (CompilationUnitTree t : trees) {
|
||||||
JCCompilationUnit cu = (JCCompilationUnit)t;
|
JCCompilationUnit cu = (JCCompilationUnit)t;
|
||||||
for (JCTree def : cu.defs) {
|
for (JCTree def : cu.defs) {
|
||||||
if (def.hasTag(CLASSDEF) &&
|
if (def.hasTag(CLASSDEF) &&
|
||||||
analyzedElems.contains(((JCTree.JCClassDecl)def).sym)) {
|
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 @@ public class CheckAttributedTree {
|
||||||
* left uninitialized after attribution
|
* left uninitialized after attribution
|
||||||
*/
|
*/
|
||||||
private class NPETester extends TreeScanner {
|
private class NPETester extends TreeScanner {
|
||||||
void test(List<Pair<JCCompilationUnit, JCTree>> trees) {
|
void test(JCCompilationUnit cut, JCTree tree) {
|
||||||
for (Pair<JCCompilationUnit, JCTree> p : trees) {
|
sourcefile = cut.sourcefile;
|
||||||
sourcefile = p.fst.sourcefile;
|
endPosTable = cut.endPositions;
|
||||||
endPosTable = p.fst.endPositions;
|
encl = new Info(tree, endPosTable);
|
||||||
encl = new Info(p.snd, endPosTable);
|
tree.accept(this);
|
||||||
p.snd.accept(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -536,21 +528,6 @@ public class CheckAttributedTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
* GUI viewer for issues found by TreePosTester. The viewer provides a drop
|
||||||
* down list for selecting error conditions, a header area providing details
|
* down list for selecting error conditions, a header area providing details
|
||||||
|
|
|
@ -238,9 +238,9 @@ public class DiamondAndInnerClassTest extends ComboInstance<DiamondAndInnerClass
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
check(newCompilationTask()
|
newCompilationTask()
|
||||||
.withSourceFromTemplate("#{DECL}")
|
.withSourceFromTemplate("#{DECL}")
|
||||||
.analyze());
|
.analyze(this::check);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(Result<?> res) {
|
void check(Result<?> res) {
|
||||||
|
|
|
@ -241,9 +241,9 @@ public class TestUncheckedCalls extends ComboInstance<TestUncheckedCalls> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws Throwable {
|
public void doWork() throws Throwable {
|
||||||
check(newCompilationTask()
|
newCompilationTask()
|
||||||
.withSourceFromTemplate(sourceTemplate)
|
.withSourceFromTemplate(sourceTemplate)
|
||||||
.analyze());
|
.analyze(this::check);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(Result<Iterable<? extends Element>> result) {
|
void check(Result<Iterable<? extends Element>> result) {
|
||||||
|
|
|
@ -189,11 +189,11 @@ public class GenericOverrideTest extends ComboInstance<GenericOverrideTest> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
check(newCompilationTask()
|
newCompilationTask()
|
||||||
.withOption("-XDuseUnsharedTable") //this test relies on predictable name indexes!
|
.withOption("-XDuseUnsharedTable") //this test relies on predictable name indexes!
|
||||||
.withOptions(level.opts)
|
.withOptions(level.opts)
|
||||||
.withSourceFromTemplate(template)
|
.withSourceFromTemplate(template)
|
||||||
.analyze());
|
.analyze(this::check);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(Result<?> res) {
|
void check(Result<?> res) {
|
||||||
|
|
|
@ -188,11 +188,11 @@ public class FunctionalInterfaceConversionTest extends ComboInstance<FunctionalI
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
check(newCompilationTask()
|
newCompilationTask()
|
||||||
.withSourceFromTemplate("Sam", samSource)
|
.withSourceFromTemplate("Sam", samSource)
|
||||||
.withSourceFromTemplate("PackageClass", pkgClassSource)
|
.withSourceFromTemplate("PackageClass", pkgClassSource)
|
||||||
.withSourceFromTemplate("Client", clientSource, this::importStmt)
|
.withSourceFromTemplate("Client", clientSource, this::importStmt)
|
||||||
.analyze());
|
.analyze(this::check);
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboParameter importStmt(String name) {
|
ComboParameter importStmt(String name) {
|
||||||
|
|
|
@ -246,9 +246,9 @@ public class LambdaParserTest extends ComboInstance<LambdaParserTest> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
check(newCompilationTask()
|
newCompilationTask()
|
||||||
.withSourceFromTemplate(template)
|
.withSourceFromTemplate(template)
|
||||||
.parse());
|
.parse(this::check);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(Result<?> res) {
|
void check(Result<?> res) {
|
||||||
|
|
|
@ -203,9 +203,9 @@ public class MethodReferenceParserTest extends ComboInstance<MethodReferencePars
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
check(newCompilationTask()
|
newCompilationTask()
|
||||||
.withSourceFromTemplate(template)
|
.withSourceFromTemplate(template)
|
||||||
.parse());
|
.parse(this::check);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(Result<?> res) {
|
void check(Result<?> res) {
|
||||||
|
|
|
@ -252,17 +252,16 @@ public class TestInvokeDynamic extends ComboInstance<TestInvokeDynamic> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
ComboTask comboTask = newCompilationTask()
|
newCompilationTask()
|
||||||
.withOption("-g")
|
.withOption("-g")
|
||||||
.withSourceFromTemplate(source_template);
|
.withSourceFromTemplate(source_template)
|
||||||
|
.withListenerFactory(context -> {
|
||||||
JavacTaskImpl ct = (JavacTaskImpl)comboTask.getTask();
|
|
||||||
Context context = ct.getContext();
|
|
||||||
Symtab syms = Symtab.instance(context);
|
Symtab syms = Symtab.instance(context);
|
||||||
Names names = Names.instance(context);
|
Names names = Names.instance(context);
|
||||||
Types types = Types.instance(context);
|
Types types = Types.instance(context);
|
||||||
ct.addTaskListener(new Indifier(syms, names, types));
|
return new Indifier(syms, names, types);
|
||||||
verifyBytecode(comboTask.generate());
|
})
|
||||||
|
.generate(this::verifyBytecode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res) {
|
void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res) {
|
||||||
|
|
|
@ -121,10 +121,10 @@ public class TestLambdaToMethodStats extends ComboInstance<TestLambdaToMethodSta
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
check(newCompilationTask()
|
newCompilationTask()
|
||||||
.withOption("--debug:dumpLambdaToMethodStats")
|
.withOption("--debug:dumpLambdaToMethodStats")
|
||||||
.withSourceFromTemplate(template)
|
.withSourceFromTemplate(template)
|
||||||
.generate());
|
.generate(this::check);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(Result<?> res) {
|
void check(Result<?> res) {
|
||||||
|
|
|
@ -193,9 +193,9 @@ public class TestLambdaBytecode extends ComboInstance<TestLambdaBytecode> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
verifyBytecode(newCompilationTask()
|
newCompilationTask()
|
||||||
.withSourceFromTemplate(source_template)
|
.withSourceFromTemplate(source_template)
|
||||||
.generate());
|
.generate(this::verifyBytecode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res) {
|
void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res) {
|
||||||
|
|
|
@ -208,10 +208,10 @@ public class StructuralMostSpecificTest extends ComboInstance<StructuralMostSpec
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws Throwable {
|
public void doWork() throws Throwable {
|
||||||
check(newCompilationTask()
|
newCompilationTask()
|
||||||
.withSourceFromTemplate(sourceTemplate)
|
.withSourceFromTemplate(sourceTemplate)
|
||||||
.withOption("--debug:verboseResolution=all,-predef,-internal,-object-init")
|
.withOption("--debug:verboseResolution=all,-predef,-internal,-object-init")
|
||||||
.analyze());
|
.analyze(this::check);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(Result<Iterable<? extends Element>> result) {
|
void check(Result<Iterable<? extends Element>> result) {
|
||||||
|
|
|
@ -281,14 +281,14 @@ public class TypeInferenceComboTest extends ComboInstance<TypeInferenceComboTest
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
Result<?> res = newCompilationTask()
|
newCompilationTask()
|
||||||
.withSourceFromTemplate("Sam", sam_template, this::samClass)
|
.withSourceFromTemplate("Sam", sam_template, this::samClass)
|
||||||
.withSourceFromTemplate("Client", client_template, this::clientContext)
|
.withSourceFromTemplate("Client", client_template, this::clientContext)
|
||||||
.analyze();
|
.analyze(res -> {
|
||||||
|
|
||||||
if (res.hasErrors() == checkTypeInference()) {
|
if (res.hasErrors() == checkTypeInference()) {
|
||||||
fail("Unexpected compilation output when compiling instance: " + res.compilationInfo());
|
fail("Unexpected compilation output when compiling instance: " + res.compilationInfo());
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboParameter samClass(String parameterName) {
|
ComboParameter samClass(String parameterName) {
|
||||||
|
|
|
@ -26,8 +26,9 @@ package combo;
|
||||||
import com.sun.source.tree.CompilationUnitTree;
|
import com.sun.source.tree.CompilationUnitTree;
|
||||||
import com.sun.source.util.JavacTask;
|
import com.sun.source.util.JavacTask;
|
||||||
import com.sun.source.util.TaskListener;
|
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.Assert;
|
||||||
|
import com.sun.tools.javac.util.Context;
|
||||||
import com.sun.tools.javac.util.List;
|
import com.sun.tools.javac.util.List;
|
||||||
import combo.ComboParameter.Resolver;
|
import combo.ComboParameter.Resolver;
|
||||||
|
|
||||||
|
@ -40,16 +41,11 @@ import javax.tools.SimpleJavaFileObject;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.net.URI;
|
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.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.Collectors;
|
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
|
* This class represents a compilation task associated with a combo test instance. This is a small
|
||||||
|
@ -73,8 +69,8 @@ public class ComboTask {
|
||||||
/** Listeners associated with this task. */
|
/** Listeners associated with this task. */
|
||||||
private List<TaskListener> listeners = List.nil();
|
private List<TaskListener> listeners = List.nil();
|
||||||
|
|
||||||
/** Underlying javac task object. */
|
/** Listener factories associated with this task. */
|
||||||
private JavacTask task;
|
private List<Function<Context, TaskListener>> listenerFactories = List.nil();
|
||||||
|
|
||||||
/** Combo execution environment. */
|
/** Combo execution environment. */
|
||||||
private ComboTestHelper<?>.Env env;
|
private ComboTestHelper<?>.Env env;
|
||||||
|
@ -169,78 +165,56 @@ public class ComboTask {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Parse the sources associated with this task.
|
||||||
*/
|
*/
|
||||||
public Result<Iterable<? extends CompilationUnitTree>> parse() throws IOException {
|
public void parse(Consumer<Result<Iterable<? extends CompilationUnitTree>>> c) {
|
||||||
return new Result<>(getTask().parse());
|
doRunTest(c, JavacTask::parse);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse and analyzes the sources associated with this task.
|
* Parse and analyzes the sources associated with this task.
|
||||||
*/
|
*/
|
||||||
public Result<Iterable<? extends Element>> analyze() throws IOException {
|
public void analyze(Consumer<Result<Iterable<? extends Element>>> c) {
|
||||||
return new Result<>(getTask().analyze());
|
doRunTest(c, JavacTask::analyze);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse, analyze and perform code generation for the sources associated with this task.
|
* Parse, analyze and perform code generation for the sources associated with this task.
|
||||||
*/
|
*/
|
||||||
public Result<Iterable<? extends JavaFileObject>> generate() throws IOException {
|
public void generate(Consumer<Result<Iterable<? extends JavaFileObject>>> c) {
|
||||||
return new Result<>(getTask().generate());
|
doRunTest(c, JavacTask::generate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private <V> void doRunTest(Consumer<Result<Iterable<? extends V>>> c,
|
||||||
* Parse, analyze, perform code generation for the sources associated with this task and finally
|
Convertor<V> task2Data) {
|
||||||
* executes them
|
env.pool().getTask(out, env.fileManager(),
|
||||||
*/
|
diagsCollector, options, null, sources, task -> {
|
||||||
public <Z> Optional<Z> execute(Function<ExecutionTask, Z> executionFunc) throws IOException {
|
try {
|
||||||
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()])))));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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());
|
|
||||||
}
|
|
||||||
context.opts = opts;
|
|
||||||
JavacTask javacTask = ((JavacTool)env.javaCompiler()).getTask(out, env.fileManager(),
|
|
||||||
diagsCollector, options, null, sources, context);
|
|
||||||
javacTask.setTaskListener(context);
|
|
||||||
for (TaskListener l : listeners) {
|
for (TaskListener l : listeners) {
|
||||||
javacTask.addTaskListener(l);
|
task.addTaskListener(l);
|
||||||
}
|
}
|
||||||
task = javacTask;
|
for (Function<Context, TaskListener> f : listenerFactories) {
|
||||||
|
task.addTaskListener(f.apply(((JavacTaskImpl) task).getContext()));
|
||||||
}
|
}
|
||||||
return task;
|
c.accept(new Result<>(task2Data.convert(task)));
|
||||||
|
return null;
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new AssertionError(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Convertor<V> {
|
||||||
|
public Iterable<? extends V> convert(JavacTask task) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -28,6 +28,7 @@ import javax.tools.StandardJavaFileManager;
|
||||||
import javax.tools.ToolProvider;
|
import javax.tools.ToolProvider;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -38,6 +39,12 @@ import java.util.function.Consumer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
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
|
* An helper class for defining combinatorial (aka "combo" tests). A combo test is made up of one
|
||||||
|
@ -93,8 +100,8 @@ public class ComboTestHelper<X extends ComboInstance<X>> {
|
||||||
/** Shared file manager used across all combo test instances. */
|
/** Shared file manager used across all combo test instances. */
|
||||||
StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
|
StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
|
||||||
|
|
||||||
/** Shared context used across all combo instances. */
|
/** JavacTask pool shared across all combo instances. */
|
||||||
ReusableContext context = new ReusableContext();
|
JavacTaskPool pool = new JavacTaskPool(1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set failure mode for this combo test.
|
* Set failure mode for this combo test.
|
||||||
|
@ -248,7 +255,7 @@ public class ComboTestHelper<X extends ComboInstance<X>> {
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new AssertionError("Failure when closing down shared file manager; ", ex);
|
throw new AssertionError("Failure when closing down shared file manager; ", ex);
|
||||||
} finally {
|
} finally {
|
||||||
info.dump();
|
info.dump(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,19 +382,16 @@ public class ComboTestHelper<X extends ComboInstance<X>> {
|
||||||
int passCount;
|
int passCount;
|
||||||
int comboCount;
|
int comboCount;
|
||||||
int skippedCount;
|
int skippedCount;
|
||||||
int ctxReusedCount;
|
|
||||||
int ctxDroppedCount;
|
|
||||||
Optional<String> lastFailure = Optional.empty();
|
Optional<String> lastFailure = Optional.empty();
|
||||||
Optional<Throwable> lastError = 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 total checks executed", comboCount));
|
||||||
System.err.println(String.format("%d successes found", passCount));
|
System.err.println(String.format("%d successes found", passCount));
|
||||||
System.err.println(String.format("%d failures found", failCount));
|
System.err.println(String.format("%d failures found", failCount));
|
||||||
System.err.println(String.format("%d errors found", errCount));
|
System.err.println(String.format("%d errors found", errCount));
|
||||||
System.err.println(String.format("%d skips found", skippedCount));
|
System.err.println(String.format("%d skips found", skippedCount));
|
||||||
System.err.println(String.format("%d contexts shared", ctxReusedCount));
|
helper.pool.printStatistics(System.err);
|
||||||
System.err.println(String.format("%d contexts dropped", ctxDroppedCount));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasFailures() {
|
public boolean hasFailures() {
|
||||||
|
@ -400,7 +404,7 @@ public class ComboTestHelper<X extends ComboInstance<X>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* 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).
|
* only if one or more dimensions are subclasses of the {@code ComboParameter} interface).
|
||||||
*/
|
*/
|
||||||
|
@ -430,12 +434,8 @@ public class ComboTestHelper<X extends ComboInstance<X>> {
|
||||||
return comp;
|
return comp;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReusableContext context() {
|
JavacTaskPool pool() {
|
||||||
return context;
|
return pool;
|
||||||
}
|
|
||||||
|
|
||||||
ReusableContext setContext(ReusableContext context) {
|
|
||||||
return ComboTestHelper.this.context = context;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -128,9 +128,9 @@ public class DisjunctiveTypeWellFormednessTest extends ComboInstance<Disjunctive
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
check(newCompilationTask()
|
newCompilationTask()
|
||||||
.withSourceFromTemplate(template)
|
.withSourceFromTemplate(template)
|
||||||
.analyze());
|
.analyze(this::check);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(Result<?> res) {
|
void check(Result<?> res) {
|
||||||
|
|
|
@ -100,13 +100,14 @@ public class BitWiseOperators extends ComboInstance<BitWiseOperators> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
Result<?> res = newCompilationTask()
|
newCompilationTask()
|
||||||
.withSourceFromTemplate(template)
|
.withSourceFromTemplate(template)
|
||||||
.analyze();
|
.analyze(res -> {
|
||||||
if (res.hasErrors() == OperandType.compatible(opTypes[0], opTypes[1])) {
|
if (res.hasErrors() == OperandType.compatible(opTypes[0], opTypes[1])) {
|
||||||
fail("Unexpected behavior. Type1: " + opTypes[0] +
|
fail("Unexpected behavior. Type1: " + opTypes[0] +
|
||||||
"; type2: " + opTypes[1] +
|
"; type2: " + opTypes[1] +
|
||||||
"; " + res.compilationInfo());
|
"; " + res.compilationInfo());
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
86
test/langtools/tools/javac/tree/ScopeClassHeaderTest.java
Normal file
86
test/langtools/tools/javac/tree/ScopeClassHeaderTest.java
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
91
test/langtools/tools/javac/util/JavacTaskPoolTest.java
Normal file
91
test/langtools/tools/javac/util/JavacTaskPoolTest.java
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -224,9 +224,9 @@ public class T7042566 extends ComboInstance<T7042566> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
check(newCompilationTask()
|
newCompilationTask()
|
||||||
.withSourceFromTemplate(source_template, this::getMethodDecl)
|
.withSourceFromTemplate(source_template, this::getMethodDecl)
|
||||||
.generate());
|
.generate(this::check);
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboParameter getMethodDecl(String parameterName) {
|
ComboParameter getMethodDecl(String parameterName) {
|
||||||
|
|
|
@ -231,12 +231,12 @@ public class Warn4 extends ComboInstance<Warn4> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
check(newCompilationTask()
|
newCompilationTask()
|
||||||
.withOption("-Xlint:unchecked")
|
.withOption("-Xlint:unchecked")
|
||||||
.withOption("-source")
|
.withOption("-source")
|
||||||
.withOption(sourceLevel.sourceKey)
|
.withOption(sourceLevel.sourceKey)
|
||||||
.withSourceFromTemplate(template)
|
.withSourceFromTemplate(template)
|
||||||
.analyze());
|
.analyze(this::check);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(Result<?> res) {
|
void check(Result<?> res) {
|
||||||
|
|
|
@ -235,12 +235,12 @@ public class Warn5 extends ComboInstance<Warn5> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doWork() throws IOException {
|
public void doWork() throws IOException {
|
||||||
check(newCompilationTask()
|
newCompilationTask()
|
||||||
.withOption(xlint.getXlintOption())
|
.withOption(xlint.getXlintOption())
|
||||||
.withOption("-source")
|
.withOption("-source")
|
||||||
.withOption(sourceLevel.sourceKey)
|
.withOption(sourceLevel.sourceKey)
|
||||||
.withSourceFromTemplate(template)
|
.withSourceFromTemplate(template)
|
||||||
.analyze());
|
.analyze(this::check);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(Result<?> res) {
|
void check(Result<?> res) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue