8183126: port improvements to analyzers from lvti repo to jdk10
Reviewed-by: mcimadamore, jlahoda
This commit is contained in:
parent
86f7b05126
commit
e5342258d1
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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() {} };
|
||||
}
|
||||
|
@ -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
|
||||
|
15
langtools/test/tools/javac/lambda/LambdaConv29.java
Normal file
15
langtools/test/tools/javac/lambda/LambdaConv29.java
Normal 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() {} };
|
||||
}
|
4
langtools/test/tools/javac/lambda/LambdaConv29.out
Normal file
4
langtools/test/tools/javac/lambda/LambdaConv29.out
Normal file
@ -0,0 +1,4 @@
|
||||
LambdaConv29.java:14:24: compiler.warn.potential.lambda.found
|
||||
- compiler.err.warnings.and.werror
|
||||
1 error
|
||||
1 warning
|
Loading…
x
Reference in New Issue
Block a user