8183126: port improvements to analyzers from lvti repo to jdk10

Reviewed-by: mcimadamore, jlahoda
This commit is contained in:
Vicente Romero 2017-07-05 19:57:47 -07:00
parent 86f7b05126
commit e5342258d1
6 changed files with 181 additions and 25 deletions

View File

@ -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.HashMap;
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 @@ public class Analyzer {
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 @@ public class Analyzer {
* 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);
}
}
/**
* 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
JCBlock fakeBlock = make.Block(SYNTHETIC, List.of(statement));
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 @@ public class Analyzer {
* 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 @@ public class Analyzer {
@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 @@ public class Analyzer {
@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 @@ public class Analyzer {
}
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;
}
}
}

View File

@ -286,6 +286,10 @@ public class JavaCompiler {
*/
protected Attr attr;
/** The analyzer
*/
protected Analyzer analyzer;
/** The attributor.
*/
protected Check chk;
@ -401,6 +405,7 @@ public class JavaCompiler {
}
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 @@ public class JavaCompiler {
if (shouldStop(CompileState.FLOW))
return;
analyzer.flush(env);
results.add(env);
}
finally {

View File

@ -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() {} };
}

View File

@ -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

View File

@ -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() {} };
}

View File

@ -0,0 +1,4 @@
LambdaConv29.java:14:24: compiler.warn.potential.lambda.found
- compiler.err.warnings.and.werror
1 error
1 warning