8183126: port improvements to analyzers from lvti repo to jdk10
authorvromero
Wed, 05 Jul 2017 19:57:47 -0700
changeset 45862 c6827bac317d
parent 45861 a82ccda077c9
child 45863 76b8b8786abb
8183126: port improvements to analyzers from lvti repo to jdk10 Reviewed-by: mcimadamore, jlahoda
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java
langtools/test/tools/javac/lambda/LambdaConv18.java
langtools/test/tools/javac/lambda/LambdaConv18.out
langtools/test/tools/javac/lambda/LambdaConv29.java
langtools/test/tools/javac/lambda/LambdaConv29.out
--- 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