8183126: port improvements to analyzers from lvti repo to jdk10
Reviewed-by: mcimadamore, jlahoda
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java Wed Jul 05 14:36:54 2017 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java Wed Jul 05 19:57:47 2017 -0700
@@ -25,6 +25,8 @@
package com.sun.tools.javac.comp;
+import java.util.ArrayList;
+
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.tools.javac.code.Source;
import com.sun.tools.javac.code.Type;
@@ -69,13 +71,24 @@
import java.util.Map;
import java.util.function.Predicate;
+import com.sun.source.tree.NewClassTree;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Kinds.Kind;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.tree.JCTree.JCTry;
+import com.sun.tools.javac.tree.JCTree.JCUnary;
+import com.sun.tools.javac.util.Assert;
+import com.sun.tools.javac.util.DiagnosticSource;
+
import static com.sun.tools.javac.code.Flags.GENERATEDCONSTR;
-import static com.sun.tools.javac.code.Flags.SYNTHETIC;
import static com.sun.tools.javac.code.TypeTag.CLASS;
import static com.sun.tools.javac.tree.JCTree.Tag.APPLY;
+import static com.sun.tools.javac.tree.JCTree.Tag.LABELLED;
import static com.sun.tools.javac.tree.JCTree.Tag.METHODDEF;
import static com.sun.tools.javac.tree.JCTree.Tag.NEWCLASS;
+import static com.sun.tools.javac.tree.JCTree.Tag.NULLCHK;
import static com.sun.tools.javac.tree.JCTree.Tag.TYPEAPPLY;
+import static com.sun.tools.javac.tree.JCTree.Tag.VARDEF;
/**
* Helper class for defining custom code analysis, such as finding instance creation expression
@@ -341,7 +354,8 @@
Env<AttrContext> copyEnvIfNeeded(JCTree tree, Env<AttrContext> env) {
if (!analyzerModes.isEmpty() &&
!env.info.isSpeculative &&
- TreeInfo.isStatement(tree)) {
+ TreeInfo.isStatement(tree) &&
+ !tree.hasTag(LABELLED)) {
Env<AttrContext> analyzeEnv =
env.dup(env.tree, env.info.dup(env.info.scope.dupUnshared(env.info.scope.owner)));
analyzeEnv.info.returnResult = analyzeEnv.info.returnResult != null ?
@@ -368,27 +382,109 @@
* and speculatively type-check the rewritten code to compare results against previously attributed code.
*/
void analyze(JCStatement statement, Env<AttrContext> env) {
- AnalysisContext context = new AnalysisContext();
+ AnalysisContext context = new AnalysisContext(statement, env);
StatementScanner statementScanner = new StatementScanner(context);
statementScanner.scan(statement);
if (!context.treesToAnalyzer.isEmpty()) {
+ deferredAnalysisHelper.queue(context);
+ }
+ }
- //add a block to hoist potential dangling variable declarations
- JCBlock fakeBlock = make.Block(SYNTHETIC, List.of(statement));
+ /**
+ * Helper interface to handle deferral of analysis tasks.
+ */
+ interface DeferredAnalysisHelper {
+ /**
+ * Add a new analysis task to the queue.
+ */
+ void queue(AnalysisContext context);
+ /**
+ * Flush queue with given attribution env.
+ */
+ void flush(Env<AttrContext> flushEnv);
+ }
+
+ /**
+ * Dummy deferral handler.
+ */
+ DeferredAnalysisHelper flushDeferredHelper = new DeferredAnalysisHelper() {
+ @Override
+ public void queue(AnalysisContext context) {
+ //do nothing
+ }
+
+ @Override
+ public void flush(Env<AttrContext> flushEnv) {
+ //do nothing
+ }
+ };
+
+ /**
+ * Simple deferral handler. All tasks belonging to the same outermost class are added to
+ * the same queue. The queue is flushed after flow analysis (only if no error occurred).
+ */
+ DeferredAnalysisHelper queueDeferredHelper = new DeferredAnalysisHelper() {
+
+ Map<ClassSymbol, ArrayList<AnalysisContext>> Q = new HashMap<>();
+
+ @Override
+ public void queue(AnalysisContext context) {
+ ArrayList<AnalysisContext> s = Q.computeIfAbsent(context.env.enclClass.sym.outermostClass(), k -> new ArrayList<>());
+ s.add(context);
+ }
+
+ @Override
+ public void flush(Env<AttrContext> flushEnv) {
+ if (!Q.isEmpty()) {
+ DeferredAnalysisHelper prevHelper = deferredAnalysisHelper;
+ try {
+ deferredAnalysisHelper = flushDeferredHelper;
+ ArrayList<AnalysisContext> s = Q.get(flushEnv.enclClass.sym.outermostClass());
+ while (s != null && !s.isEmpty()) {
+ doAnalysis(s.remove(0));
+ }
+ } finally {
+ deferredAnalysisHelper = prevHelper;
+ }
+ }
+ }
+ };
+
+ DeferredAnalysisHelper deferredAnalysisHelper = queueDeferredHelper;
+
+ void doAnalysis(AnalysisContext context) {
+ DiagnosticSource prevSource = log.currentSource();
+ LocalCacheContext localCacheContext = argumentAttr.withLocalCacheContext();
+ try {
+ log.useSource(context.env.toplevel.getSourceFile());
+
+ JCStatement treeToAnalyze = (JCStatement)context.tree;
+ if (context.env.info.scope.owner.kind == Kind.TYP) {
+ //add a block to hoist potential dangling variable declarations
+ treeToAnalyze = make.Block(Flags.SYNTHETIC, List.of((JCStatement)context.tree));
+ }
TreeMapper treeMapper = new TreeMapper(context);
//TODO: to further refine the analysis, try all rewriting combinations
- deferredAttr.attribSpeculative(fakeBlock, env, attr.statInfo, treeMapper,
- t -> new AnalyzeDeferredDiagHandler(context),
- argumentAttr.withLocalCacheContext());
+ deferredAttr.attribSpeculative(treeToAnalyze, context.env, attr.statInfo, treeMapper,
+ t -> new AnalyzeDeferredDiagHandler(context), argumentAttr.withLocalCacheContext());
context.treeMap.entrySet().forEach(e -> {
context.treesToAnalyzer.get(e.getKey())
.process(e.getKey(), e.getValue(), context.errors.nonEmpty());
});
+ } catch (Throwable ex) {
+ Assert.error("Analyzer error when processing: " + context.tree);
+ } finally {
+ log.useSource(prevSource.getFile());
+ localCacheContext.leave();
}
}
+ public void flush(Env<AttrContext> flushEnv) {
+ deferredAnalysisHelper.flush(flushEnv);
+ }
+
/**
* Simple deferred diagnostic handler which filters out all messages and keep track of errors.
*/
@@ -411,6 +507,23 @@
* trees to be rewritten, errors occurred during the speculative attribution step, etc.
*/
class AnalysisContext {
+
+ JCTree tree;
+
+ Env<AttrContext> env;
+
+ AnalysisContext(JCTree tree, Env<AttrContext> env) {
+ this.tree = tree;
+ this.env = attr.copyEnv(env);
+ /* this is a temporary workaround that should be removed once we have a truly independent
+ * clone operation
+ */
+ if (tree.hasTag(VARDEF)) {
+ // avoid redefinition clashes
+ this.env.info.scope.remove(((JCVariableDecl)tree).sym);
+ }
+ }
+
/** Map from trees to analyzers. */
Map<JCTree, StatementAnalyzer<JCTree, JCTree>> treesToAnalyzer = new HashMap<>();
@@ -452,17 +565,17 @@
@Override
public void visitClassDef(JCClassDecl tree) {
- //do nothing (prevents seeing same stuff twice
+ //do nothing (prevents seeing same stuff twice)
}
@Override
public void visitMethodDef(JCMethodDecl tree) {
- //do nothing (prevents seeing same stuff twice
+ //do nothing (prevents seeing same stuff twice)
}
@Override
public void visitBlock(JCBlock tree) {
- //do nothing (prevents seeing same stuff twice
+ //do nothing (prevents seeing same stuff twice)
}
@Override
@@ -472,28 +585,40 @@
@Override
public void visitForLoop(JCForLoop tree) {
- scan(tree.getInitializer());
+ //skip body and var decl (to prevents same statements to be analyzed twice)
scan(tree.getCondition());
scan(tree.getUpdate());
}
@Override
+ public void visitTry(JCTry tree) {
+ //skip resources (to prevents same statements to be analyzed twice)
+ scan(tree.getBlock());
+ scan(tree.getCatches());
+ scan(tree.getFinallyBlock());
+ }
+
+ @Override
public void visitForeachLoop(JCEnhancedForLoop tree) {
+ //skip body (to prevents same statements to be analyzed twice)
scan(tree.getExpression());
}
@Override
public void visitWhileLoop(JCWhileLoop tree) {
+ //skip body (to prevents same statements to be analyzed twice)
scan(tree.getCondition());
}
@Override
public void visitDoLoop(JCDoWhileLoop tree) {
+ //skip body (to prevents same statements to be analyzed twice)
scan(tree.getCondition());
}
@Override
public void visitIf(JCIf tree) {
+ //skip body (to prevents same statements to be analyzed twice)
scan(tree.getCondition());
}
}
@@ -533,5 +658,17 @@
}
return newLambda;
}
+
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public JCTree visitNewClass(NewClassTree node, Void aVoid) {
+ JCNewClass oldNewClazz = (JCNewClass)node;
+ JCNewClass newNewClazz = (JCNewClass)super.visitNewClass(node, aVoid);
+ if (!oldNewClazz.args.isEmpty() && oldNewClazz.args.head.hasTag(NULLCHK)) {
+ //workaround to Attr generating trees
+ newNewClazz.encl = ((JCUnary)newNewClazz.args.head).arg;
+ newNewClazz.args = newNewClazz.args.tail;
+ }
+ return newNewClazz;
+ }
}
}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java Wed Jul 05 14:36:54 2017 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java Wed Jul 05 19:57:47 2017 -0700
@@ -286,6 +286,10 @@
*/
protected Attr attr;
+ /** The analyzer
+ */
+ protected Analyzer analyzer;
+
/** The attributor.
*/
protected Check chk;
@@ -401,6 +405,7 @@
}
source = Source.instance(context);
attr = Attr.instance(context);
+ analyzer = Analyzer.instance(context);
chk = Check.instance(context);
gen = Gen.instance(context);
flow = Flow.instance(context);
@@ -1392,6 +1397,8 @@
if (shouldStop(CompileState.FLOW))
return;
+ analyzer.flush(env);
+
results.add(env);
}
finally {
--- a/langtools/test/tools/javac/lambda/LambdaConv18.java Wed Jul 05 14:36:54 2017 -0700
+++ b/langtools/test/tools/javac/lambda/LambdaConv18.java Wed Jul 05 19:57:47 2017 -0700
@@ -1,24 +1,19 @@
/*
* @test /nodynamiccopyright/
- * @bug 8003280 8064365
+ * @bug 8003280 8064365 8183126
* @summary Add lambda tests
* simple test for lambda candidate check
- * @compile/fail/ref=LambdaConv18.out -XDrawDiagnostics -XDfind=lambda LambdaConv18.java
+ * @compile/fail/ref=LambdaConv18.out -XDrawDiagnostics LambdaConv18.java
*/
class LambdaConv18 {
- interface SAM {
- void m();
- }
-
interface NonSAM {
void m1();
void m2();
}
- SAM s1 = new SAM() { public void m() {} };
- NonSAM s2 = new NonSAM() { public void m1() {}
+ NonSAM s1 = new NonSAM() { public void m1() {}
public void m2() {} };
- NonExistent s3 = new NonExistent() { public void m() {} };
+ NonExistent s2 = new NonExistent() { public void m() {} };
}
--- a/langtools/test/tools/javac/lambda/LambdaConv18.out Wed Jul 05 14:36:54 2017 -0700
+++ b/langtools/test/tools/javac/lambda/LambdaConv18.out Wed Jul 05 19:57:47 2017 -0700
@@ -1,5 +1,3 @@
-LambdaConv18.java:23:5: compiler.err.cant.resolve.location: kindname.class, NonExistent, , , (compiler.misc.location: kindname.class, LambdaConv18, null)
-LambdaConv18.java:20:24: compiler.warn.potential.lambda.found
-LambdaConv18.java:23:26: compiler.err.cant.resolve.location: kindname.class, NonExistent, , , (compiler.misc.location: kindname.class, LambdaConv18, null)
+LambdaConv18.java:18:5: compiler.err.cant.resolve.location: kindname.class, NonExistent, , , (compiler.misc.location: kindname.class, LambdaConv18, null)
+LambdaConv18.java:18:26: compiler.err.cant.resolve.location: kindname.class, NonExistent, , , (compiler.misc.location: kindname.class, LambdaConv18, null)
2 errors
-1 warning
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/LambdaConv29.java Wed Jul 05 19:57:47 2017 -0700
@@ -0,0 +1,15 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8183126
+ * @summary test for lambda finder
+ * @compile/fail/ref=LambdaConv29.out -XDrawDiagnostics -Werror -XDfind=lambda LambdaConv29.java
+ */
+
+class LambdaConv29 {
+
+ interface SAM {
+ void m();
+ }
+
+ SAM s1 = new SAM() { public void m() {} };
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/LambdaConv29.out Wed Jul 05 19:57:47 2017 -0700
@@ -0,0 +1,4 @@
+LambdaConv29.java:14:24: compiler.warn.potential.lambda.found
+- compiler.err.warnings.and.werror
+1 error
+1 warning