diff -r 36de9c637393 -r bc25e62f4794 src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java Fri Oct 27 08:21:53 2017 +0530 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java Thu Oct 26 22:54:25 2017 -0400 @@ -58,12 +58,10 @@ 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.JCDiagnostic; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Log; -import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Options; import java.util.EnumSet; @@ -104,7 +102,7 @@ final DeferredAttr deferredAttr; final ArgumentAttr argumentAttr; final TreeMaker make; - final Names names; + final AnalyzerCopier copier; private final boolean allowDiamondWithAnonymousClassCreation; final EnumSet analyzerModes; @@ -124,7 +122,7 @@ deferredAttr = DeferredAttr.instance(context); argumentAttr = ArgumentAttr.instance(context); make = TreeMaker.instance(context); - names = Names.instance(context); + copier = new AnalyzerCopier(); Options options = Options.instance(context); String findOpt = options.get("find"); //parse modes @@ -205,15 +203,14 @@ abstract boolean match(S tree); /** - * Rewrite a given AST node into a new one + * Rewrite a given AST node into a new one(s) */ - abstract T map(S oldTree, S newTree); + abstract List rewrite(S oldTree); /** * Entry-point for comparing results and generating diagnostics. */ abstract void process(S oldTree, T newTree, boolean hasErrors); - } /** @@ -233,11 +230,14 @@ } @Override - JCNewClass map(JCNewClass oldTree, JCNewClass newTree) { - if (newTree.clazz.hasTag(TYPEAPPLY)) { - ((JCTypeApply)newTree.clazz).arguments = List.nil(); + List rewrite(JCNewClass oldTree) { + if (oldTree.clazz.hasTag(TYPEAPPLY)) { + JCNewClass nc = copier.copy(oldTree); + ((JCTypeApply)nc.clazz).arguments = List.nil(); + return List.of(nc); + } else { + return List.of(oldTree); } - return newTree; } @Override @@ -301,12 +301,14 @@ } @Override - JCLambda map (JCNewClass oldTree, JCNewClass newTree){ - JCMethodDecl md = (JCMethodDecl)decls(newTree.def).head; + List rewrite(JCNewClass oldTree){ + JCMethodDecl md = (JCMethodDecl)decls(oldTree.def).head; List params = md.params; JCBlock body = md.body; - return make.Lambda(params, body); + JCLambda newTree = make.Lambda(params, body); + return List.of(newTree); } + @Override void process (JCNewClass oldTree, JCLambda newTree, boolean hasErrors){ if (!hasErrors) { @@ -330,10 +332,12 @@ tree.typeargs.nonEmpty(); } @Override - JCMethodInvocation map (JCMethodInvocation oldTree, JCMethodInvocation newTree){ - newTree.typeargs = List.nil(); - return newTree; + List rewrite(JCMethodInvocation oldTree){ + JCMethodInvocation app = copier.copy(oldTree); + app.typeargs = List.nil(); + return List.of(app); } + @Override void process (JCMethodInvocation oldTree, JCMethodInvocation newTree, boolean hasErrors){ if (!hasErrors) { @@ -355,7 +359,8 @@ /** * Map a variable tree into a new declaration using implicit type. */ - JCVariableDecl mapVar(JCVariableDecl oldTree, JCVariableDecl newTree){ + JCVariableDecl rewriteVarType(JCVariableDecl oldTree) { + JCVariableDecl newTree = copier.copy(oldTree); newTree.vartype = null; return newTree; } @@ -363,7 +368,7 @@ /** * Analyze results of local variable inference. */ - void processVar(JCVariableDecl oldTree, JCVariableDecl newTree, boolean hasErrors){ + void processVar(JCVariableDecl oldTree, JCVariableDecl newTree, boolean hasErrors) { if (!hasErrors) { if (types.isSameType(oldTree.type, newTree.type)) { log.warning(oldTree, Warnings.LocalRedundantType); @@ -387,8 +392,8 @@ attr.canInferLocalVarType(tree) == null; } @Override - JCVariableDecl map(JCVariableDecl oldTree, JCVariableDecl newTree){ - return mapVar(oldTree, newTree); + List rewrite(JCVariableDecl oldTree) { + return List.of(rewriteVarType(oldTree)); } @Override void process(JCVariableDecl oldTree, JCVariableDecl newTree, boolean hasErrors){ @@ -410,10 +415,11 @@ return !tree.var.isImplicitlyTyped(); } @Override - JCEnhancedForLoop map(JCEnhancedForLoop oldTree, JCEnhancedForLoop newTree){ - newTree.var = mapVar(oldTree.var, newTree.var); - newTree.body = make.Block(0, List.nil()); //ignore body for analysis purpose - return newTree; + List rewrite(JCEnhancedForLoop oldTree) { + JCEnhancedForLoop newTree = copier.copy(oldTree); + newTree.var = rewriteVarType(oldTree.var); + newTree.body = make.Block(0, List.nil()); + return List.of(newTree); } @Override void process(JCEnhancedForLoop oldTree, JCEnhancedForLoop newTree, boolean hasErrors){ @@ -464,12 +470,13 @@ * and speculatively type-check the rewritten code to compare results against previously attributed code. */ void analyze(JCStatement statement, Env env) { - AnalysisContext context = new AnalysisContext(statement, env); - StatementScanner statementScanner = new StatementScanner(context); - statementScanner.scan(statement); + StatementScanner statementScanner = new StatementScanner(statement, env); + statementScanner.scan(); - if (!context.treesToAnalyzer.isEmpty()) { - deferredAnalysisHelper.queue(context); + if (!statementScanner.rewritings.isEmpty()) { + for (RewritingContext rewriting : statementScanner.rewritings) { + deferredAnalysisHelper.queue(rewriting); + } } } @@ -480,7 +487,7 @@ /** * Add a new analysis task to the queue. */ - void queue(AnalysisContext context); + void queue(RewritingContext rewriting); /** * Flush queue with given attribution env. */ @@ -492,7 +499,7 @@ */ DeferredAnalysisHelper flushDeferredHelper = new DeferredAnalysisHelper() { @Override - public void queue(AnalysisContext context) { + public void queue(RewritingContext rewriting) { //do nothing } @@ -508,12 +515,12 @@ */ DeferredAnalysisHelper queueDeferredHelper = new DeferredAnalysisHelper() { - Map> Q = new HashMap<>(); + Map> Q = new HashMap<>(); @Override - public void queue(AnalysisContext context) { - ArrayList s = Q.computeIfAbsent(context.env.enclClass.sym.outermostClass(), k -> new ArrayList<>()); - s.add(context); + public void queue(RewritingContext rewriting) { + ArrayList s = Q.computeIfAbsent(rewriting.env.enclClass.sym.outermostClass(), k -> new ArrayList<>()); + s.add(rewriting); } @Override @@ -522,9 +529,9 @@ DeferredAnalysisHelper prevHelper = deferredAnalysisHelper; try { deferredAnalysisHelper = flushDeferredHelper; - ArrayList s = Q.get(flushEnv.enclClass.sym.outermostClass()); - while (s != null && !s.isEmpty()) { - doAnalysis(s.remove(0)); + ArrayList rewritings = Q.get(flushEnv.enclClass.sym.outermostClass()); + while (rewritings != null && !rewritings.isEmpty()) { + doAnalysis(rewritings.remove(0)); } } finally { deferredAnalysisHelper = prevHelper; @@ -535,28 +542,24 @@ DeferredAnalysisHelper deferredAnalysisHelper = queueDeferredHelper; - void doAnalysis(AnalysisContext context) { + void doAnalysis(RewritingContext rewriting) { DiagnosticSource prevSource = log.currentSource(); LocalCacheContext localCacheContext = argumentAttr.withLocalCacheContext(); try { - log.useSource(context.env.toplevel.getSourceFile()); + log.useSource(rewriting.env.toplevel.getSourceFile()); - JCStatement treeToAnalyze = (JCStatement)context.tree; - if (context.env.info.scope.owner.kind == Kind.TYP) { + JCStatement treeToAnalyze = (JCStatement)rewriting.originalTree; + if (rewriting.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)); + treeToAnalyze = make.Block(Flags.SYNTHETIC, List.of((JCStatement)rewriting.originalTree)); } - TreeMapper treeMapper = new TreeMapper(context); //TODO: to further refine the analysis, try all rewriting combinations - 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()); - }); + deferredAttr.attribSpeculative(treeToAnalyze, rewriting.env, attr.statInfo, new TreeRewriter(rewriting), + t -> rewriting.diagHandler(), argumentAttr.withLocalCacheContext()); + rewriting.analyzer.process(rewriting.oldTree, rewriting.replacement, rewriting.erroneous); } catch (Throwable ex) { - Assert.error("Analyzer error when processing: " + context.tree); + Assert.error("Analyzer error when processing: " + rewriting.originalTree); } finally { log.useSource(prevSource.getFile()); localCacheContext.leave(); @@ -568,65 +571,22 @@ } /** - * Simple deferred diagnostic handler which filters out all messages and keep track of errors. - */ - class AnalyzeDeferredDiagHandler extends Log.DeferredDiagnosticHandler { - AnalysisContext context; - - public AnalyzeDeferredDiagHandler(AnalysisContext context) { - super(log, d -> { - if (d.getType() == DiagnosticType.ERROR) { - context.errors.add(d); - } - return true; - }); - this.context = context; - } - } - - /** - * This class is used to pass around contextual information bewteen analyzer classes, such as - * trees to be rewritten, errors occurred during the speculative attribution step, etc. - */ - class AnalysisContext { - - JCTree tree; - - Env env; - - AnalysisContext(JCTree tree, Env 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> treesToAnalyzer = new HashMap<>(); - - /** Map from original AST nodes to rewritten AST nodes */ - Map treeMap = new HashMap<>(); - - /** Errors in rewritten tree */ - ListBuffer errors = new ListBuffer<>(); - } - - /** * Subclass of {@link com.sun.tools.javac.tree.TreeScanner} which visit AST-nodes w/o crossing * statement boundaries. */ class StatementScanner extends TreeScanner { + /** Tree rewritings (generated by analyzers). */ + ListBuffer rewritings = new ListBuffer<>(); + JCTree originalTree; + Env env; - /** context */ - AnalysisContext context; + StatementScanner(JCTree originalTree, Env env) { + this.originalTree = originalTree; + this.env = attr.copyEnv(env); + } - StatementScanner(AnalysisContext context) { - this.context = context; + public void scan() { + scan(originalTree); } @Override @@ -637,7 +597,9 @@ if (analyzer.isEnabled() && tree.hasTag(analyzer.tag) && analyzer.match(tree)) { - context.treesToAnalyzer.put(tree, analyzer); + for (JCTree t : analyzer.rewrite(tree)) { + rewritings.add(new RewritingContext(originalTree, tree, t, analyzer, env)); + } break; //TODO: cover cases where multiple matching analyzers are found } } @@ -705,28 +667,60 @@ } } + class RewritingContext { + // the whole tree being analyzed + JCTree originalTree; + // a subtree, old tree, that will be rewritten + JCTree oldTree; + // the replacement for the old tree + JCTree replacement; + // did the compiler find any error + boolean erroneous; + // the env + Env env; + // the corresponding analyzer + StatementAnalyzer analyzer; + + RewritingContext( + JCTree originalTree, + JCTree oldTree, + JCTree replacement, + StatementAnalyzer analyzer, + Env env) { + this.originalTree = originalTree; + this.oldTree = oldTree; + this.replacement = replacement; + this.analyzer = analyzer; + this.env = attr.copyEnv(env); + /* this is a temporary workaround that should be removed once we have a truly independent + * clone operation + */ + if (originalTree.hasTag(VARDEF)) { + // avoid redefinition clashes + this.env.info.scope.remove(((JCVariableDecl)originalTree).sym); + } + } + + /** + * Simple deferred diagnostic handler which filters out all messages and keep track of errors. + */ + Log.DeferredDiagnosticHandler diagHandler() { + return new Log.DeferredDiagnosticHandler(log, d -> { + if (d.getType() == DiagnosticType.ERROR) { + erroneous = true; + } + return true; + }); + } + } + /** * Subclass of TreeCopier that maps nodes matched by analyzers onto new AST nodes. */ - class TreeMapper extends TreeCopier { - - AnalysisContext context; - - TreeMapper(AnalysisContext context) { - super(make); - this.context = context; - } + class AnalyzerCopier extends TreeCopier { - @Override - @SuppressWarnings("unchecked") - public Z copy(Z tree, Void _unused) { - Z newTree = super.copy(tree, _unused); - StatementAnalyzer analyzer = context.treesToAnalyzer.get(tree); - if (analyzer != null) { - newTree = (Z)analyzer.map(tree, newTree); - context.treeMap.put(tree, newTree); - } - return newTree; + public AnalyzerCopier() { + super(make); } @Override @DefinedBy(Api.COMPILER_TREE) @@ -753,4 +747,24 @@ return newNewClazz; } } + + class TreeRewriter extends AnalyzerCopier { + + RewritingContext rewriting; + + TreeRewriter(RewritingContext rewriting) { + this.rewriting = rewriting; + } + + @Override + @SuppressWarnings("unchecked") + public Z copy(Z tree, Void _unused) { + Z newTree = super.copy(tree, null); + if (tree != null && tree == rewriting.oldTree) { + Assert.checkNonNull(rewriting.replacement); + newTree = (Z)rewriting.replacement; + } + return newTree; + } + } }