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;
|
package com.sun.tools.javac.comp;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import com.sun.source.tree.LambdaExpressionTree;
|
import com.sun.source.tree.LambdaExpressionTree;
|
||||||
import com.sun.tools.javac.code.Source;
|
import com.sun.tools.javac.code.Source;
|
||||||
import com.sun.tools.javac.code.Type;
|
import com.sun.tools.javac.code.Type;
|
||||||
@ -69,13 +71,24 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Predicate;
|
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.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.code.TypeTag.CLASS;
|
||||||
import static com.sun.tools.javac.tree.JCTree.Tag.APPLY;
|
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.METHODDEF;
|
||||||
import static com.sun.tools.javac.tree.JCTree.Tag.NEWCLASS;
|
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.TYPEAPPLY;
|
||||||
|
import static com.sun.tools.javac.tree.JCTree.Tag.VARDEF;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for defining custom code analysis, such as finding instance creation expression
|
* 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) {
|
Env<AttrContext> copyEnvIfNeeded(JCTree tree, Env<AttrContext> env) {
|
||||||
if (!analyzerModes.isEmpty() &&
|
if (!analyzerModes.isEmpty() &&
|
||||||
!env.info.isSpeculative &&
|
!env.info.isSpeculative &&
|
||||||
TreeInfo.isStatement(tree)) {
|
TreeInfo.isStatement(tree) &&
|
||||||
|
!tree.hasTag(LABELLED)) {
|
||||||
Env<AttrContext> analyzeEnv =
|
Env<AttrContext> analyzeEnv =
|
||||||
env.dup(env.tree, env.info.dup(env.info.scope.dupUnshared(env.info.scope.owner)));
|
env.dup(env.tree, env.info.dup(env.info.scope.dupUnshared(env.info.scope.owner)));
|
||||||
analyzeEnv.info.returnResult = analyzeEnv.info.returnResult != null ?
|
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.
|
* and speculatively type-check the rewritten code to compare results against previously attributed code.
|
||||||
*/
|
*/
|
||||||
void analyze(JCStatement statement, Env<AttrContext> env) {
|
void analyze(JCStatement statement, Env<AttrContext> env) {
|
||||||
AnalysisContext context = new AnalysisContext();
|
AnalysisContext context = new AnalysisContext(statement, env);
|
||||||
StatementScanner statementScanner = new StatementScanner(context);
|
StatementScanner statementScanner = new StatementScanner(context);
|
||||||
statementScanner.scan(statement);
|
statementScanner.scan(statement);
|
||||||
|
|
||||||
if (!context.treesToAnalyzer.isEmpty()) {
|
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);
|
TreeMapper treeMapper = new TreeMapper(context);
|
||||||
//TODO: to further refine the analysis, try all rewriting combinations
|
//TODO: to further refine the analysis, try all rewriting combinations
|
||||||
deferredAttr.attribSpeculative(fakeBlock, env, attr.statInfo, treeMapper,
|
deferredAttr.attribSpeculative(treeToAnalyze, context.env, attr.statInfo, treeMapper,
|
||||||
t -> new AnalyzeDeferredDiagHandler(context),
|
t -> new AnalyzeDeferredDiagHandler(context), argumentAttr.withLocalCacheContext());
|
||||||
argumentAttr.withLocalCacheContext());
|
|
||||||
context.treeMap.entrySet().forEach(e -> {
|
context.treeMap.entrySet().forEach(e -> {
|
||||||
context.treesToAnalyzer.get(e.getKey())
|
context.treesToAnalyzer.get(e.getKey())
|
||||||
.process(e.getKey(), e.getValue(), context.errors.nonEmpty());
|
.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.
|
* 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.
|
* trees to be rewritten, errors occurred during the speculative attribution step, etc.
|
||||||
*/
|
*/
|
||||||
class AnalysisContext {
|
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 from trees to analyzers. */
|
||||||
Map<JCTree, StatementAnalyzer<JCTree, JCTree>> treesToAnalyzer = new HashMap<>();
|
Map<JCTree, StatementAnalyzer<JCTree, JCTree>> treesToAnalyzer = new HashMap<>();
|
||||||
|
|
||||||
@ -452,17 +565,17 @@ public class Analyzer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitClassDef(JCClassDecl tree) {
|
public void visitClassDef(JCClassDecl tree) {
|
||||||
//do nothing (prevents seeing same stuff twice
|
//do nothing (prevents seeing same stuff twice)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitMethodDef(JCMethodDecl tree) {
|
public void visitMethodDef(JCMethodDecl tree) {
|
||||||
//do nothing (prevents seeing same stuff twice
|
//do nothing (prevents seeing same stuff twice)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitBlock(JCBlock tree) {
|
public void visitBlock(JCBlock tree) {
|
||||||
//do nothing (prevents seeing same stuff twice
|
//do nothing (prevents seeing same stuff twice)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -472,28 +585,40 @@ public class Analyzer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitForLoop(JCForLoop tree) {
|
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.getCondition());
|
||||||
scan(tree.getUpdate());
|
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
|
@Override
|
||||||
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
public void visitForeachLoop(JCEnhancedForLoop tree) {
|
||||||
|
//skip body (to prevents same statements to be analyzed twice)
|
||||||
scan(tree.getExpression());
|
scan(tree.getExpression());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitWhileLoop(JCWhileLoop tree) {
|
public void visitWhileLoop(JCWhileLoop tree) {
|
||||||
|
//skip body (to prevents same statements to be analyzed twice)
|
||||||
scan(tree.getCondition());
|
scan(tree.getCondition());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitDoLoop(JCDoWhileLoop tree) {
|
public void visitDoLoop(JCDoWhileLoop tree) {
|
||||||
|
//skip body (to prevents same statements to be analyzed twice)
|
||||||
scan(tree.getCondition());
|
scan(tree.getCondition());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitIf(JCIf tree) {
|
public void visitIf(JCIf tree) {
|
||||||
|
//skip body (to prevents same statements to be analyzed twice)
|
||||||
scan(tree.getCondition());
|
scan(tree.getCondition());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -533,5 +658,17 @@ public class Analyzer {
|
|||||||
}
|
}
|
||||||
return newLambda;
|
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;
|
protected Attr attr;
|
||||||
|
|
||||||
|
/** The analyzer
|
||||||
|
*/
|
||||||
|
protected Analyzer analyzer;
|
||||||
|
|
||||||
/** The attributor.
|
/** The attributor.
|
||||||
*/
|
*/
|
||||||
protected Check chk;
|
protected Check chk;
|
||||||
@ -401,6 +405,7 @@ public class JavaCompiler {
|
|||||||
}
|
}
|
||||||
source = Source.instance(context);
|
source = Source.instance(context);
|
||||||
attr = Attr.instance(context);
|
attr = Attr.instance(context);
|
||||||
|
analyzer = Analyzer.instance(context);
|
||||||
chk = Check.instance(context);
|
chk = Check.instance(context);
|
||||||
gen = Gen.instance(context);
|
gen = Gen.instance(context);
|
||||||
flow = Flow.instance(context);
|
flow = Flow.instance(context);
|
||||||
@ -1392,6 +1397,8 @@ public class JavaCompiler {
|
|||||||
if (shouldStop(CompileState.FLOW))
|
if (shouldStop(CompileState.FLOW))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
analyzer.flush(env);
|
||||||
|
|
||||||
results.add(env);
|
results.add(env);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
@ -1,24 +1,19 @@
|
|||||||
/*
|
/*
|
||||||
* @test /nodynamiccopyright/
|
* @test /nodynamiccopyright/
|
||||||
* @bug 8003280 8064365
|
* @bug 8003280 8064365 8183126
|
||||||
* @summary Add lambda tests
|
* @summary Add lambda tests
|
||||||
* simple test for lambda candidate check
|
* 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 {
|
class LambdaConv18 {
|
||||||
|
|
||||||
interface SAM {
|
|
||||||
void m();
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NonSAM {
|
interface NonSAM {
|
||||||
void m1();
|
void m1();
|
||||||
void m2();
|
void m2();
|
||||||
}
|
}
|
||||||
|
|
||||||
SAM s1 = new SAM() { public void m() {} };
|
NonSAM s1 = new NonSAM() { public void m1() {}
|
||||||
NonSAM s2 = new NonSAM() { public void m1() {}
|
|
||||||
public void m2() {} };
|
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:18: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:18:26: compiler.err.cant.resolve.location: kindname.class, NonExistent, , , (compiler.misc.location: kindname.class, LambdaConv18, null)
|
||||||
LambdaConv18.java:23:26: compiler.err.cant.resolve.location: kindname.class, NonExistent, , , (compiler.misc.location: kindname.class, LambdaConv18, null)
|
|
||||||
2 errors
|
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