67 import java.util.EnumSet; |
69 import java.util.EnumSet; |
68 import java.util.HashMap; |
70 import java.util.HashMap; |
69 import java.util.Map; |
71 import java.util.Map; |
70 import java.util.function.Predicate; |
72 import java.util.function.Predicate; |
71 |
73 |
|
74 import com.sun.source.tree.NewClassTree; |
|
75 import com.sun.tools.javac.code.Flags; |
|
76 import com.sun.tools.javac.code.Kinds.Kind; |
|
77 import com.sun.tools.javac.code.Symbol.ClassSymbol; |
|
78 import com.sun.tools.javac.tree.JCTree.JCTry; |
|
79 import com.sun.tools.javac.tree.JCTree.JCUnary; |
|
80 import com.sun.tools.javac.util.Assert; |
|
81 import com.sun.tools.javac.util.DiagnosticSource; |
|
82 |
72 import static com.sun.tools.javac.code.Flags.GENERATEDCONSTR; |
83 import static com.sun.tools.javac.code.Flags.GENERATEDCONSTR; |
73 import static com.sun.tools.javac.code.Flags.SYNTHETIC; |
|
74 import static com.sun.tools.javac.code.TypeTag.CLASS; |
84 import static com.sun.tools.javac.code.TypeTag.CLASS; |
75 import static com.sun.tools.javac.tree.JCTree.Tag.APPLY; |
85 import static com.sun.tools.javac.tree.JCTree.Tag.APPLY; |
|
86 import static com.sun.tools.javac.tree.JCTree.Tag.LABELLED; |
76 import static com.sun.tools.javac.tree.JCTree.Tag.METHODDEF; |
87 import static com.sun.tools.javac.tree.JCTree.Tag.METHODDEF; |
77 import static com.sun.tools.javac.tree.JCTree.Tag.NEWCLASS; |
88 import static com.sun.tools.javac.tree.JCTree.Tag.NEWCLASS; |
|
89 import static com.sun.tools.javac.tree.JCTree.Tag.NULLCHK; |
78 import static com.sun.tools.javac.tree.JCTree.Tag.TYPEAPPLY; |
90 import static com.sun.tools.javac.tree.JCTree.Tag.TYPEAPPLY; |
|
91 import static com.sun.tools.javac.tree.JCTree.Tag.VARDEF; |
79 |
92 |
80 /** |
93 /** |
81 * Helper class for defining custom code analysis, such as finding instance creation expression |
94 * Helper class for defining custom code analysis, such as finding instance creation expression |
82 * that can benefit from diamond syntax. |
95 * that can benefit from diamond syntax. |
83 */ |
96 */ |
339 * Create a copy of Env if needed. |
352 * Create a copy of Env if needed. |
340 */ |
353 */ |
341 Env<AttrContext> copyEnvIfNeeded(JCTree tree, Env<AttrContext> env) { |
354 Env<AttrContext> copyEnvIfNeeded(JCTree tree, Env<AttrContext> env) { |
342 if (!analyzerModes.isEmpty() && |
355 if (!analyzerModes.isEmpty() && |
343 !env.info.isSpeculative && |
356 !env.info.isSpeculative && |
344 TreeInfo.isStatement(tree)) { |
357 TreeInfo.isStatement(tree) && |
|
358 !tree.hasTag(LABELLED)) { |
345 Env<AttrContext> analyzeEnv = |
359 Env<AttrContext> analyzeEnv = |
346 env.dup(env.tree, env.info.dup(env.info.scope.dupUnshared(env.info.scope.owner))); |
360 env.dup(env.tree, env.info.dup(env.info.scope.dupUnshared(env.info.scope.owner))); |
347 analyzeEnv.info.returnResult = analyzeEnv.info.returnResult != null ? |
361 analyzeEnv.info.returnResult = analyzeEnv.info.returnResult != null ? |
348 attr.new ResultInfo(analyzeEnv.info.returnResult.pkind, |
362 attr.new ResultInfo(analyzeEnv.info.returnResult.pkind, |
349 analyzeEnv.info.returnResult.pt) : null; |
363 analyzeEnv.info.returnResult.pt) : null; |
366 /** |
380 /** |
367 * Analyze an AST node; this involves collecting a list of all the nodes that needs rewriting, |
381 * Analyze an AST node; this involves collecting a list of all the nodes that needs rewriting, |
368 * and speculatively type-check the rewritten code to compare results against previously attributed code. |
382 * and speculatively type-check the rewritten code to compare results against previously attributed code. |
369 */ |
383 */ |
370 void analyze(JCStatement statement, Env<AttrContext> env) { |
384 void analyze(JCStatement statement, Env<AttrContext> env) { |
371 AnalysisContext context = new AnalysisContext(); |
385 AnalysisContext context = new AnalysisContext(statement, env); |
372 StatementScanner statementScanner = new StatementScanner(context); |
386 StatementScanner statementScanner = new StatementScanner(context); |
373 statementScanner.scan(statement); |
387 statementScanner.scan(statement); |
374 |
388 |
375 if (!context.treesToAnalyzer.isEmpty()) { |
389 if (!context.treesToAnalyzer.isEmpty()) { |
376 |
390 deferredAnalysisHelper.queue(context); |
377 //add a block to hoist potential dangling variable declarations |
391 } |
378 JCBlock fakeBlock = make.Block(SYNTHETIC, List.of(statement)); |
392 } |
|
393 |
|
394 /** |
|
395 * Helper interface to handle deferral of analysis tasks. |
|
396 */ |
|
397 interface DeferredAnalysisHelper { |
|
398 /** |
|
399 * Add a new analysis task to the queue. |
|
400 */ |
|
401 void queue(AnalysisContext context); |
|
402 /** |
|
403 * Flush queue with given attribution env. |
|
404 */ |
|
405 void flush(Env<AttrContext> flushEnv); |
|
406 } |
|
407 |
|
408 /** |
|
409 * Dummy deferral handler. |
|
410 */ |
|
411 DeferredAnalysisHelper flushDeferredHelper = new DeferredAnalysisHelper() { |
|
412 @Override |
|
413 public void queue(AnalysisContext context) { |
|
414 //do nothing |
|
415 } |
|
416 |
|
417 @Override |
|
418 public void flush(Env<AttrContext> flushEnv) { |
|
419 //do nothing |
|
420 } |
|
421 }; |
|
422 |
|
423 /** |
|
424 * Simple deferral handler. All tasks belonging to the same outermost class are added to |
|
425 * the same queue. The queue is flushed after flow analysis (only if no error occurred). |
|
426 */ |
|
427 DeferredAnalysisHelper queueDeferredHelper = new DeferredAnalysisHelper() { |
|
428 |
|
429 Map<ClassSymbol, ArrayList<AnalysisContext>> Q = new HashMap<>(); |
|
430 |
|
431 @Override |
|
432 public void queue(AnalysisContext context) { |
|
433 ArrayList<AnalysisContext> s = Q.computeIfAbsent(context.env.enclClass.sym.outermostClass(), k -> new ArrayList<>()); |
|
434 s.add(context); |
|
435 } |
|
436 |
|
437 @Override |
|
438 public void flush(Env<AttrContext> flushEnv) { |
|
439 if (!Q.isEmpty()) { |
|
440 DeferredAnalysisHelper prevHelper = deferredAnalysisHelper; |
|
441 try { |
|
442 deferredAnalysisHelper = flushDeferredHelper; |
|
443 ArrayList<AnalysisContext> s = Q.get(flushEnv.enclClass.sym.outermostClass()); |
|
444 while (s != null && !s.isEmpty()) { |
|
445 doAnalysis(s.remove(0)); |
|
446 } |
|
447 } finally { |
|
448 deferredAnalysisHelper = prevHelper; |
|
449 } |
|
450 } |
|
451 } |
|
452 }; |
|
453 |
|
454 DeferredAnalysisHelper deferredAnalysisHelper = queueDeferredHelper; |
|
455 |
|
456 void doAnalysis(AnalysisContext context) { |
|
457 DiagnosticSource prevSource = log.currentSource(); |
|
458 LocalCacheContext localCacheContext = argumentAttr.withLocalCacheContext(); |
|
459 try { |
|
460 log.useSource(context.env.toplevel.getSourceFile()); |
|
461 |
|
462 JCStatement treeToAnalyze = (JCStatement)context.tree; |
|
463 if (context.env.info.scope.owner.kind == Kind.TYP) { |
|
464 //add a block to hoist potential dangling variable declarations |
|
465 treeToAnalyze = make.Block(Flags.SYNTHETIC, List.of((JCStatement)context.tree)); |
|
466 } |
379 |
467 |
380 TreeMapper treeMapper = new TreeMapper(context); |
468 TreeMapper treeMapper = new TreeMapper(context); |
381 //TODO: to further refine the analysis, try all rewriting combinations |
469 //TODO: to further refine the analysis, try all rewriting combinations |
382 deferredAttr.attribSpeculative(fakeBlock, env, attr.statInfo, treeMapper, |
470 deferredAttr.attribSpeculative(treeToAnalyze, context.env, attr.statInfo, treeMapper, |
383 t -> new AnalyzeDeferredDiagHandler(context), |
471 t -> new AnalyzeDeferredDiagHandler(context), argumentAttr.withLocalCacheContext()); |
384 argumentAttr.withLocalCacheContext()); |
|
385 context.treeMap.entrySet().forEach(e -> { |
472 context.treeMap.entrySet().forEach(e -> { |
386 context.treesToAnalyzer.get(e.getKey()) |
473 context.treesToAnalyzer.get(e.getKey()) |
387 .process(e.getKey(), e.getValue(), context.errors.nonEmpty()); |
474 .process(e.getKey(), e.getValue(), context.errors.nonEmpty()); |
388 }); |
475 }); |
389 } |
476 } catch (Throwable ex) { |
|
477 Assert.error("Analyzer error when processing: " + context.tree); |
|
478 } finally { |
|
479 log.useSource(prevSource.getFile()); |
|
480 localCacheContext.leave(); |
|
481 } |
|
482 } |
|
483 |
|
484 public void flush(Env<AttrContext> flushEnv) { |
|
485 deferredAnalysisHelper.flush(flushEnv); |
390 } |
486 } |
391 |
487 |
392 /** |
488 /** |
393 * Simple deferred diagnostic handler which filters out all messages and keep track of errors. |
489 * Simple deferred diagnostic handler which filters out all messages and keep track of errors. |
394 */ |
490 */ |
409 /** |
505 /** |
410 * This class is used to pass around contextual information bewteen analyzer classes, such as |
506 * This class is used to pass around contextual information bewteen analyzer classes, such as |
411 * trees to be rewritten, errors occurred during the speculative attribution step, etc. |
507 * trees to be rewritten, errors occurred during the speculative attribution step, etc. |
412 */ |
508 */ |
413 class AnalysisContext { |
509 class AnalysisContext { |
|
510 |
|
511 JCTree tree; |
|
512 |
|
513 Env<AttrContext> env; |
|
514 |
|
515 AnalysisContext(JCTree tree, Env<AttrContext> env) { |
|
516 this.tree = tree; |
|
517 this.env = attr.copyEnv(env); |
|
518 /* this is a temporary workaround that should be removed once we have a truly independent |
|
519 * clone operation |
|
520 */ |
|
521 if (tree.hasTag(VARDEF)) { |
|
522 // avoid redefinition clashes |
|
523 this.env.info.scope.remove(((JCVariableDecl)tree).sym); |
|
524 } |
|
525 } |
|
526 |
414 /** Map from trees to analyzers. */ |
527 /** Map from trees to analyzers. */ |
415 Map<JCTree, StatementAnalyzer<JCTree, JCTree>> treesToAnalyzer = new HashMap<>(); |
528 Map<JCTree, StatementAnalyzer<JCTree, JCTree>> treesToAnalyzer = new HashMap<>(); |
416 |
529 |
417 /** Map from original AST nodes to rewritten AST nodes */ |
530 /** Map from original AST nodes to rewritten AST nodes */ |
418 Map<JCTree, JCTree> treeMap = new HashMap<>(); |
531 Map<JCTree, JCTree> treeMap = new HashMap<>(); |
450 super.scan(tree); |
563 super.scan(tree); |
451 } |
564 } |
452 |
565 |
453 @Override |
566 @Override |
454 public void visitClassDef(JCClassDecl tree) { |
567 public void visitClassDef(JCClassDecl tree) { |
455 //do nothing (prevents seeing same stuff twice |
568 //do nothing (prevents seeing same stuff twice) |
456 } |
569 } |
457 |
570 |
458 @Override |
571 @Override |
459 public void visitMethodDef(JCMethodDecl tree) { |
572 public void visitMethodDef(JCMethodDecl tree) { |
460 //do nothing (prevents seeing same stuff twice |
573 //do nothing (prevents seeing same stuff twice) |
461 } |
574 } |
462 |
575 |
463 @Override |
576 @Override |
464 public void visitBlock(JCBlock tree) { |
577 public void visitBlock(JCBlock tree) { |
465 //do nothing (prevents seeing same stuff twice |
578 //do nothing (prevents seeing same stuff twice) |
466 } |
579 } |
467 |
580 |
468 @Override |
581 @Override |
469 public void visitSwitch(JCSwitch tree) { |
582 public void visitSwitch(JCSwitch tree) { |
470 scan(tree.getExpression()); |
583 scan(tree.getExpression()); |
471 } |
584 } |
472 |
585 |
473 @Override |
586 @Override |
474 public void visitForLoop(JCForLoop tree) { |
587 public void visitForLoop(JCForLoop tree) { |
475 scan(tree.getInitializer()); |
588 //skip body and var decl (to prevents same statements to be analyzed twice) |
476 scan(tree.getCondition()); |
589 scan(tree.getCondition()); |
477 scan(tree.getUpdate()); |
590 scan(tree.getUpdate()); |
478 } |
591 } |
479 |
592 |
480 @Override |
593 @Override |
|
594 public void visitTry(JCTry tree) { |
|
595 //skip resources (to prevents same statements to be analyzed twice) |
|
596 scan(tree.getBlock()); |
|
597 scan(tree.getCatches()); |
|
598 scan(tree.getFinallyBlock()); |
|
599 } |
|
600 |
|
601 @Override |
481 public void visitForeachLoop(JCEnhancedForLoop tree) { |
602 public void visitForeachLoop(JCEnhancedForLoop tree) { |
|
603 //skip body (to prevents same statements to be analyzed twice) |
482 scan(tree.getExpression()); |
604 scan(tree.getExpression()); |
483 } |
605 } |
484 |
606 |
485 @Override |
607 @Override |
486 public void visitWhileLoop(JCWhileLoop tree) { |
608 public void visitWhileLoop(JCWhileLoop tree) { |
|
609 //skip body (to prevents same statements to be analyzed twice) |
487 scan(tree.getCondition()); |
610 scan(tree.getCondition()); |
488 } |
611 } |
489 |
612 |
490 @Override |
613 @Override |
491 public void visitDoLoop(JCDoWhileLoop tree) { |
614 public void visitDoLoop(JCDoWhileLoop tree) { |
|
615 //skip body (to prevents same statements to be analyzed twice) |
492 scan(tree.getCondition()); |
616 scan(tree.getCondition()); |
493 } |
617 } |
494 |
618 |
495 @Override |
619 @Override |
496 public void visitIf(JCIf tree) { |
620 public void visitIf(JCIf tree) { |
|
621 //skip body (to prevents same statements to be analyzed twice) |
497 scan(tree.getCondition()); |
622 scan(tree.getCondition()); |
498 } |
623 } |
499 } |
624 } |
500 |
625 |
501 /** |
626 /** |