Flow.java revision 4202:2bd34895dda2
1/* 2 * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26//todo: one might eliminate uninits.andSets when monotonic 27 28package com.sun.tools.javac.comp; 29 30import java.util.HashMap; 31 32import com.sun.source.tree.LambdaExpressionTree.BodyKind; 33import com.sun.tools.javac.code.*; 34import com.sun.tools.javac.code.Scope.WriteableScope; 35import com.sun.tools.javac.resources.CompilerProperties.Errors; 36import com.sun.tools.javac.resources.CompilerProperties.Warnings; 37import com.sun.tools.javac.tree.*; 38import com.sun.tools.javac.util.*; 39import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 40 41import com.sun.tools.javac.code.Symbol.*; 42import com.sun.tools.javac.tree.JCTree.*; 43 44import static com.sun.tools.javac.code.Flags.*; 45import static com.sun.tools.javac.code.Flags.BLOCK; 46import static com.sun.tools.javac.code.Kinds.Kind.*; 47import static com.sun.tools.javac.code.TypeTag.BOOLEAN; 48import static com.sun.tools.javac.code.TypeTag.VOID; 49import static com.sun.tools.javac.tree.JCTree.Tag.*; 50 51/** This pass implements dataflow analysis for Java programs though 52 * different AST visitor steps. Liveness analysis (see AliveAnalyzer) checks that 53 * every statement is reachable. Exception analysis (see FlowAnalyzer) ensures that 54 * every checked exception that is thrown is declared or caught. Definite assignment analysis 55 * (see AssignAnalyzer) ensures that each variable is assigned when used. Definite 56 * unassignment analysis (see AssignAnalyzer) in ensures that no final variable 57 * is assigned more than once. Finally, local variable capture analysis (see CaptureAnalyzer) 58 * determines that local variables accessed within the scope of an inner class/lambda 59 * are either final or effectively-final. 60 * 61 * <p>The JLS has a number of problems in the 62 * specification of these flow analysis problems. This implementation 63 * attempts to address those issues. 64 * 65 * <p>First, there is no accommodation for a finally clause that cannot 66 * complete normally. For liveness analysis, an intervening finally 67 * clause can cause a break, continue, or return not to reach its 68 * target. For exception analysis, an intervening finally clause can 69 * cause any exception to be "caught". For DA/DU analysis, the finally 70 * clause can prevent a transfer of control from propagating DA/DU 71 * state to the target. In addition, code in the finally clause can 72 * affect the DA/DU status of variables. 73 * 74 * <p>For try statements, we introduce the idea of a variable being 75 * definitely unassigned "everywhere" in a block. A variable V is 76 * "unassigned everywhere" in a block iff it is unassigned at the 77 * beginning of the block and there is no reachable assignment to V 78 * in the block. An assignment V=e is reachable iff V is not DA 79 * after e. Then we can say that V is DU at the beginning of the 80 * catch block iff V is DU everywhere in the try block. Similarly, V 81 * is DU at the beginning of the finally block iff V is DU everywhere 82 * in the try block and in every catch block. Specifically, the 83 * following bullet is added to 16.2.2 84 * <pre> 85 * V is <em>unassigned everywhere</em> in a block if it is 86 * unassigned before the block and there is no reachable 87 * assignment to V within the block. 88 * </pre> 89 * <p>In 16.2.15, the third bullet (and all of its sub-bullets) for all 90 * try blocks is changed to 91 * <pre> 92 * V is definitely unassigned before a catch block iff V is 93 * definitely unassigned everywhere in the try block. 94 * </pre> 95 * <p>The last bullet (and all of its sub-bullets) for try blocks that 96 * have a finally block is changed to 97 * <pre> 98 * V is definitely unassigned before the finally block iff 99 * V is definitely unassigned everywhere in the try block 100 * and everywhere in each catch block of the try statement. 101 * </pre> 102 * <p>In addition, 103 * <pre> 104 * V is definitely assigned at the end of a constructor iff 105 * V is definitely assigned after the block that is the body 106 * of the constructor and V is definitely assigned at every 107 * return that can return from the constructor. 108 * </pre> 109 * <p>In addition, each continue statement with the loop as its target 110 * is treated as a jump to the end of the loop body, and "intervening" 111 * finally clauses are treated as follows: V is DA "due to the 112 * continue" iff V is DA before the continue statement or V is DA at 113 * the end of any intervening finally block. V is DU "due to the 114 * continue" iff any intervening finally cannot complete normally or V 115 * is DU at the end of every intervening finally block. This "due to 116 * the continue" concept is then used in the spec for the loops. 117 * 118 * <p>Similarly, break statements must consider intervening finally 119 * blocks. For liveness analysis, a break statement for which any 120 * intervening finally cannot complete normally is not considered to 121 * cause the target statement to be able to complete normally. Then 122 * we say V is DA "due to the break" iff V is DA before the break or 123 * V is DA at the end of any intervening finally block. V is DU "due 124 * to the break" iff any intervening finally cannot complete normally 125 * or V is DU at the break and at the end of every intervening 126 * finally block. (I suspect this latter condition can be 127 * simplified.) This "due to the break" is then used in the spec for 128 * all statements that can be "broken". 129 * 130 * <p>The return statement is treated similarly. V is DA "due to a 131 * return statement" iff V is DA before the return statement or V is 132 * DA at the end of any intervening finally block. Note that we 133 * don't have to worry about the return expression because this 134 * concept is only used for construcrors. 135 * 136 * <p>There is no spec in the JLS for when a variable is definitely 137 * assigned at the end of a constructor, which is needed for final 138 * fields (8.3.1.2). We implement the rule that V is DA at the end 139 * of the constructor iff it is DA and the end of the body of the 140 * constructor and V is DA "due to" every return of the constructor. 141 * 142 * <p>Intervening finally blocks similarly affect exception analysis. An 143 * intervening finally that cannot complete normally allows us to ignore 144 * an otherwise uncaught exception. 145 * 146 * <p>To implement the semantics of intervening finally clauses, all 147 * nonlocal transfers (break, continue, return, throw, method call that 148 * can throw a checked exception, and a constructor invocation that can 149 * thrown a checked exception) are recorded in a queue, and removed 150 * from the queue when we complete processing the target of the 151 * nonlocal transfer. This allows us to modify the queue in accordance 152 * with the above rules when we encounter a finally clause. The only 153 * exception to this [no pun intended] is that checked exceptions that 154 * are known to be caught or declared to be caught in the enclosing 155 * method are not recorded in the queue, but instead are recorded in a 156 * global variable "{@code Set<Type> thrown}" that records the type of all 157 * exceptions that can be thrown. 158 * 159 * <p>Other minor issues the treatment of members of other classes 160 * (always considered DA except that within an anonymous class 161 * constructor, where DA status from the enclosing scope is 162 * preserved), treatment of the case expression (V is DA before the 163 * case expression iff V is DA after the switch expression), 164 * treatment of variables declared in a switch block (the implied 165 * DA/DU status after the switch expression is DU and not DA for 166 * variables defined in a switch block), the treatment of boolean ?: 167 * expressions (The JLS rules only handle b and c non-boolean; the 168 * new rule is that if b and c are boolean valued, then V is 169 * (un)assigned after a?b:c when true/false iff V is (un)assigned 170 * after b when true/false and V is (un)assigned after c when 171 * true/false). 172 * 173 * <p>There is the remaining question of what syntactic forms constitute a 174 * reference to a variable. It is conventional to allow this.x on the 175 * left-hand-side to initialize a final instance field named x, yet 176 * this.x isn't considered a "use" when appearing on a right-hand-side 177 * in most implementations. Should parentheses affect what is 178 * considered a variable reference? The simplest rule would be to 179 * allow unqualified forms only, parentheses optional, and phase out 180 * support for assigning to a final field via this.x. 181 * 182 * <p><b>This is NOT part of any supported API. 183 * If you write code that depends on this, you do so at your own risk. 184 * This code and its internal interfaces are subject to change or 185 * deletion without notice.</b> 186 */ 187public class Flow { 188 protected static final Context.Key<Flow> flowKey = new Context.Key<>(); 189 190 private final Names names; 191 private final Log log; 192 private final Symtab syms; 193 private final Types types; 194 private final Check chk; 195 private TreeMaker make; 196 private final Resolve rs; 197 private final JCDiagnostic.Factory diags; 198 private Env<AttrContext> attrEnv; 199 private Lint lint; 200 private final boolean allowImprovedRethrowAnalysis; 201 private final boolean allowImprovedCatchAnalysis; 202 private final boolean allowEffectivelyFinalInInnerClasses; 203 private final boolean enforceThisDotInit; 204 205 public static Flow instance(Context context) { 206 Flow instance = context.get(flowKey); 207 if (instance == null) 208 instance = new Flow(context); 209 return instance; 210 } 211 212 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 213 new AliveAnalyzer().analyzeTree(env, make); 214 new AssignAnalyzer().analyzeTree(env); 215 new FlowAnalyzer().analyzeTree(env, make); 216 new CaptureAnalyzer().analyzeTree(env, make); 217 } 218 219 public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) { 220 Log.DiagnosticHandler diagHandler = null; 221 //we need to disable diagnostics temporarily; the problem is that if 222 //a lambda expression contains e.g. an unreachable statement, an error 223 //message will be reported and will cause compilation to skip the flow analyis 224 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis 225 //related errors, which will allow for more errors to be detected 226 if (!speculative) { 227 diagHandler = new Log.DiscardDiagnosticHandler(log); 228 } 229 try { 230 new LambdaAliveAnalyzer().analyzeTree(env, that, make); 231 } finally { 232 if (!speculative) { 233 log.popDiagnosticHandler(diagHandler); 234 } 235 } 236 } 237 238 public List<Type> analyzeLambdaThrownTypes(final Env<AttrContext> env, 239 JCLambda that, TreeMaker make) { 240 //we need to disable diagnostics temporarily; the problem is that if 241 //a lambda expression contains e.g. an unreachable statement, an error 242 //message will be reported and will cause compilation to skip the flow analyis 243 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis 244 //related errors, which will allow for more errors to be detected 245 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); 246 try { 247 new LambdaAssignAnalyzer(env).analyzeTree(env, that); 248 LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer(); 249 flowAnalyzer.analyzeTree(env, that, make); 250 return flowAnalyzer.inferredThrownTypes; 251 } finally { 252 log.popDiagnosticHandler(diagHandler); 253 } 254 } 255 256 /** 257 * Definite assignment scan mode 258 */ 259 enum FlowKind { 260 /** 261 * This is the normal DA/DU analysis mode 262 */ 263 NORMAL("var.might.already.be.assigned", false), 264 /** 265 * This is the speculative DA/DU analysis mode used to speculatively 266 * derive assertions within loop bodies 267 */ 268 SPECULATIVE_LOOP("var.might.be.assigned.in.loop", true); 269 270 final String errKey; 271 final boolean isFinal; 272 273 FlowKind(String errKey, boolean isFinal) { 274 this.errKey = errKey; 275 this.isFinal = isFinal; 276 } 277 278 boolean isFinal() { 279 return isFinal; 280 } 281 } 282 283 protected Flow(Context context) { 284 context.put(flowKey, this); 285 names = Names.instance(context); 286 log = Log.instance(context); 287 syms = Symtab.instance(context); 288 types = Types.instance(context); 289 chk = Check.instance(context); 290 lint = Lint.instance(context); 291 rs = Resolve.instance(context); 292 diags = JCDiagnostic.Factory.instance(context); 293 Source source = Source.instance(context); 294 allowImprovedRethrowAnalysis = source.allowImprovedRethrowAnalysis(); 295 allowImprovedCatchAnalysis = source.allowImprovedCatchAnalysis(); 296 allowEffectivelyFinalInInnerClasses = source.allowEffectivelyFinalInInnerClasses(); 297 enforceThisDotInit = source.enforceThisDotInit(); 298 } 299 300 /** 301 * Base visitor class for all visitors implementing dataflow analysis logic. 302 * This class define the shared logic for handling jumps (break/continue statements). 303 */ 304 static abstract class BaseAnalyzer<P extends BaseAnalyzer.PendingExit> extends TreeScanner { 305 306 enum JumpKind { 307 BREAK(JCTree.Tag.BREAK) { 308 @Override 309 JCTree getTarget(JCTree tree) { 310 return ((JCBreak)tree).target; 311 } 312 }, 313 CONTINUE(JCTree.Tag.CONTINUE) { 314 @Override 315 JCTree getTarget(JCTree tree) { 316 return ((JCContinue)tree).target; 317 } 318 }; 319 320 final JCTree.Tag treeTag; 321 322 private JumpKind(Tag treeTag) { 323 this.treeTag = treeTag; 324 } 325 326 abstract JCTree getTarget(JCTree tree); 327 } 328 329 /** The currently pending exits that go from current inner blocks 330 * to an enclosing block, in source order. 331 */ 332 ListBuffer<P> pendingExits; 333 334 /** A pending exit. These are the statements return, break, and 335 * continue. In addition, exception-throwing expressions or 336 * statements are put here when not known to be caught. This 337 * will typically result in an error unless it is within a 338 * try-finally whose finally block cannot complete normally. 339 */ 340 static class PendingExit { 341 JCTree tree; 342 343 PendingExit(JCTree tree) { 344 this.tree = tree; 345 } 346 347 void resolveJump() { 348 //do nothing 349 } 350 } 351 352 abstract void markDead(); 353 354 /** Record an outward transfer of control. */ 355 void recordExit(P pe) { 356 pendingExits.append(pe); 357 markDead(); 358 } 359 360 /** Resolve all jumps of this statement. */ 361 private boolean resolveJump(JCTree tree, 362 ListBuffer<P> oldPendingExits, 363 JumpKind jk) { 364 boolean resolved = false; 365 List<P> exits = pendingExits.toList(); 366 pendingExits = oldPendingExits; 367 for (; exits.nonEmpty(); exits = exits.tail) { 368 P exit = exits.head; 369 if (exit.tree.hasTag(jk.treeTag) && 370 jk.getTarget(exit.tree) == tree) { 371 exit.resolveJump(); 372 resolved = true; 373 } else { 374 pendingExits.append(exit); 375 } 376 } 377 return resolved; 378 } 379 380 /** Resolve all continues of this statement. */ 381 boolean resolveContinues(JCTree tree) { 382 return resolveJump(tree, new ListBuffer<P>(), JumpKind.CONTINUE); 383 } 384 385 /** Resolve all breaks of this statement. */ 386 boolean resolveBreaks(JCTree tree, ListBuffer<P> oldPendingExits) { 387 return resolveJump(tree, oldPendingExits, JumpKind.BREAK); 388 } 389 390 @Override 391 public void scan(JCTree tree) { 392 if (tree != null && ( 393 tree.type == null || 394 tree.type != Type.stuckType)) { 395 super.scan(tree); 396 } 397 } 398 399 public void visitPackageDef(JCPackageDecl tree) { 400 // Do nothing for PackageDecl 401 } 402 } 403 404 /** 405 * This pass implements the first step of the dataflow analysis, namely 406 * the liveness analysis check. This checks that every statement is reachable. 407 * The output of this analysis pass are used by other analyzers. This analyzer 408 * sets the 'finallyCanCompleteNormally' field in the JCTry class. 409 */ 410 class AliveAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> { 411 412 /** A flag that indicates whether the last statement could 413 * complete normally. 414 */ 415 private boolean alive; 416 417 @Override 418 void markDead() { 419 alive = false; 420 } 421 422 /************************************************************************* 423 * Visitor methods for statements and definitions 424 *************************************************************************/ 425 426 /** Analyze a definition. 427 */ 428 void scanDef(JCTree tree) { 429 scanStat(tree); 430 if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && !alive) { 431 log.error(tree.pos(), 432 Errors.InitializerMustBeAbleToCompleteNormally); 433 } 434 } 435 436 /** Analyze a statement. Check that statement is reachable. 437 */ 438 void scanStat(JCTree tree) { 439 if (!alive && tree != null) { 440 log.error(tree.pos(), Errors.UnreachableStmt); 441 if (!tree.hasTag(SKIP)) alive = true; 442 } 443 scan(tree); 444 } 445 446 /** Analyze list of statements. 447 */ 448 void scanStats(List<? extends JCStatement> trees) { 449 if (trees != null) 450 for (List<? extends JCStatement> l = trees; l.nonEmpty(); l = l.tail) 451 scanStat(l.head); 452 } 453 454 /* ------------ Visitor methods for various sorts of trees -------------*/ 455 456 public void visitClassDef(JCClassDecl tree) { 457 if (tree.sym == null) return; 458 boolean alivePrev = alive; 459 ListBuffer<PendingExit> pendingExitsPrev = pendingExits; 460 Lint lintPrev = lint; 461 462 pendingExits = new ListBuffer<>(); 463 lint = lint.augment(tree.sym); 464 465 try { 466 // process all the static initializers 467 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 468 if (!l.head.hasTag(METHODDEF) && 469 (TreeInfo.flags(l.head) & STATIC) != 0) { 470 scanDef(l.head); 471 } 472 } 473 474 // process all the instance initializers 475 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 476 if (!l.head.hasTag(METHODDEF) && 477 (TreeInfo.flags(l.head) & STATIC) == 0) { 478 scanDef(l.head); 479 } 480 } 481 482 // process all the methods 483 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 484 if (l.head.hasTag(METHODDEF)) { 485 scan(l.head); 486 } 487 } 488 } finally { 489 pendingExits = pendingExitsPrev; 490 alive = alivePrev; 491 lint = lintPrev; 492 } 493 } 494 495 public void visitMethodDef(JCMethodDecl tree) { 496 if (tree.body == null) return; 497 Lint lintPrev = lint; 498 499 lint = lint.augment(tree.sym); 500 501 Assert.check(pendingExits.isEmpty()); 502 503 try { 504 alive = true; 505 scanStat(tree.body); 506 507 if (alive && !tree.sym.type.getReturnType().hasTag(VOID)) 508 log.error(TreeInfo.diagEndPos(tree.body), Errors.MissingRetStmt); 509 510 List<PendingExit> exits = pendingExits.toList(); 511 pendingExits = new ListBuffer<>(); 512 while (exits.nonEmpty()) { 513 PendingExit exit = exits.head; 514 exits = exits.tail; 515 Assert.check(exit.tree.hasTag(RETURN)); 516 } 517 } finally { 518 lint = lintPrev; 519 } 520 } 521 522 public void visitVarDef(JCVariableDecl tree) { 523 if (tree.init != null) { 524 Lint lintPrev = lint; 525 lint = lint.augment(tree.sym); 526 try{ 527 scan(tree.init); 528 } finally { 529 lint = lintPrev; 530 } 531 } 532 } 533 534 public void visitBlock(JCBlock tree) { 535 scanStats(tree.stats); 536 } 537 538 public void visitDoLoop(JCDoWhileLoop tree) { 539 ListBuffer<PendingExit> prevPendingExits = pendingExits; 540 pendingExits = new ListBuffer<>(); 541 scanStat(tree.body); 542 alive |= resolveContinues(tree); 543 scan(tree.cond); 544 alive = alive && !tree.cond.type.isTrue(); 545 alive |= resolveBreaks(tree, prevPendingExits); 546 } 547 548 public void visitWhileLoop(JCWhileLoop tree) { 549 ListBuffer<PendingExit> prevPendingExits = pendingExits; 550 pendingExits = new ListBuffer<>(); 551 scan(tree.cond); 552 alive = !tree.cond.type.isFalse(); 553 scanStat(tree.body); 554 alive |= resolveContinues(tree); 555 alive = resolveBreaks(tree, prevPendingExits) || 556 !tree.cond.type.isTrue(); 557 } 558 559 public void visitForLoop(JCForLoop tree) { 560 ListBuffer<PendingExit> prevPendingExits = pendingExits; 561 scanStats(tree.init); 562 pendingExits = new ListBuffer<>(); 563 if (tree.cond != null) { 564 scan(tree.cond); 565 alive = !tree.cond.type.isFalse(); 566 } else { 567 alive = true; 568 } 569 scanStat(tree.body); 570 alive |= resolveContinues(tree); 571 scan(tree.step); 572 alive = resolveBreaks(tree, prevPendingExits) || 573 tree.cond != null && !tree.cond.type.isTrue(); 574 } 575 576 public void visitForeachLoop(JCEnhancedForLoop tree) { 577 visitVarDef(tree.var); 578 ListBuffer<PendingExit> prevPendingExits = pendingExits; 579 scan(tree.expr); 580 pendingExits = new ListBuffer<>(); 581 scanStat(tree.body); 582 alive |= resolveContinues(tree); 583 resolveBreaks(tree, prevPendingExits); 584 alive = true; 585 } 586 587 public void visitLabelled(JCLabeledStatement tree) { 588 ListBuffer<PendingExit> prevPendingExits = pendingExits; 589 pendingExits = new ListBuffer<>(); 590 scanStat(tree.body); 591 alive |= resolveBreaks(tree, prevPendingExits); 592 } 593 594 public void visitSwitch(JCSwitch tree) { 595 ListBuffer<PendingExit> prevPendingExits = pendingExits; 596 pendingExits = new ListBuffer<>(); 597 scan(tree.selector); 598 boolean hasDefault = false; 599 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { 600 alive = true; 601 JCCase c = l.head; 602 if (c.pat == null) 603 hasDefault = true; 604 else 605 scan(c.pat); 606 scanStats(c.stats); 607 // Warn about fall-through if lint switch fallthrough enabled. 608 if (alive && 609 lint.isEnabled(Lint.LintCategory.FALLTHROUGH) && 610 c.stats.nonEmpty() && l.tail.nonEmpty()) 611 log.warning(Lint.LintCategory.FALLTHROUGH, 612 l.tail.head.pos(), 613 Warnings.PossibleFallThroughIntoCase); 614 } 615 if (!hasDefault) { 616 alive = true; 617 } 618 alive |= resolveBreaks(tree, prevPendingExits); 619 } 620 621 public void visitTry(JCTry tree) { 622 ListBuffer<PendingExit> prevPendingExits = pendingExits; 623 pendingExits = new ListBuffer<>(); 624 for (JCTree resource : tree.resources) { 625 if (resource instanceof JCVariableDecl) { 626 JCVariableDecl vdecl = (JCVariableDecl) resource; 627 visitVarDef(vdecl); 628 } else if (resource instanceof JCExpression) { 629 scan((JCExpression) resource); 630 } else { 631 throw new AssertionError(tree); // parser error 632 } 633 } 634 635 scanStat(tree.body); 636 boolean aliveEnd = alive; 637 638 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 639 alive = true; 640 JCVariableDecl param = l.head.param; 641 scan(param); 642 scanStat(l.head.body); 643 aliveEnd |= alive; 644 } 645 if (tree.finalizer != null) { 646 ListBuffer<PendingExit> exits = pendingExits; 647 pendingExits = prevPendingExits; 648 alive = true; 649 scanStat(tree.finalizer); 650 tree.finallyCanCompleteNormally = alive; 651 if (!alive) { 652 if (lint.isEnabled(Lint.LintCategory.FINALLY)) { 653 log.warning(Lint.LintCategory.FINALLY, 654 TreeInfo.diagEndPos(tree.finalizer), 655 Warnings.FinallyCannotComplete); 656 } 657 } else { 658 while (exits.nonEmpty()) { 659 pendingExits.append(exits.next()); 660 } 661 alive = aliveEnd; 662 } 663 } else { 664 alive = aliveEnd; 665 ListBuffer<PendingExit> exits = pendingExits; 666 pendingExits = prevPendingExits; 667 while (exits.nonEmpty()) pendingExits.append(exits.next()); 668 } 669 } 670 671 @Override 672 public void visitIf(JCIf tree) { 673 scan(tree.cond); 674 scanStat(tree.thenpart); 675 if (tree.elsepart != null) { 676 boolean aliveAfterThen = alive; 677 alive = true; 678 scanStat(tree.elsepart); 679 alive = alive | aliveAfterThen; 680 } else { 681 alive = true; 682 } 683 } 684 685 public void visitBreak(JCBreak tree) { 686 recordExit(new PendingExit(tree)); 687 } 688 689 public void visitContinue(JCContinue tree) { 690 recordExit(new PendingExit(tree)); 691 } 692 693 public void visitReturn(JCReturn tree) { 694 scan(tree.expr); 695 recordExit(new PendingExit(tree)); 696 } 697 698 public void visitThrow(JCThrow tree) { 699 scan(tree.expr); 700 markDead(); 701 } 702 703 public void visitApply(JCMethodInvocation tree) { 704 scan(tree.meth); 705 scan(tree.args); 706 } 707 708 public void visitNewClass(JCNewClass tree) { 709 scan(tree.encl); 710 scan(tree.args); 711 if (tree.def != null) { 712 scan(tree.def); 713 } 714 } 715 716 @Override 717 public void visitLambda(JCLambda tree) { 718 if (tree.type != null && 719 tree.type.isErroneous()) { 720 return; 721 } 722 723 ListBuffer<PendingExit> prevPending = pendingExits; 724 boolean prevAlive = alive; 725 try { 726 pendingExits = new ListBuffer<>(); 727 alive = true; 728 scanStat(tree.body); 729 tree.canCompleteNormally = alive; 730 } 731 finally { 732 pendingExits = prevPending; 733 alive = prevAlive; 734 } 735 } 736 737 public void visitModuleDef(JCModuleDecl tree) { 738 // Do nothing for modules 739 } 740 741 /************************************************************************** 742 * main method 743 *************************************************************************/ 744 745 /** Perform definite assignment/unassignment analysis on a tree. 746 */ 747 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 748 analyzeTree(env, env.tree, make); 749 } 750 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) { 751 try { 752 attrEnv = env; 753 Flow.this.make = make; 754 pendingExits = new ListBuffer<>(); 755 alive = true; 756 scan(tree); 757 } finally { 758 pendingExits = null; 759 Flow.this.make = null; 760 } 761 } 762 } 763 764 /** 765 * This pass implements the second step of the dataflow analysis, namely 766 * the exception analysis. This is to ensure that every checked exception that is 767 * thrown is declared or caught. The analyzer uses some info that has been set by 768 * the liveliness analyzer. 769 */ 770 class FlowAnalyzer extends BaseAnalyzer<FlowAnalyzer.FlowPendingExit> { 771 772 /** A flag that indicates whether the last statement could 773 * complete normally. 774 */ 775 HashMap<Symbol, List<Type>> preciseRethrowTypes; 776 777 /** The current class being defined. 778 */ 779 JCClassDecl classDef; 780 781 /** The list of possibly thrown declarable exceptions. 782 */ 783 List<Type> thrown; 784 785 /** The list of exceptions that are either caught or declared to be 786 * thrown. 787 */ 788 List<Type> caught; 789 790 class FlowPendingExit extends BaseAnalyzer.PendingExit { 791 792 Type thrown; 793 794 FlowPendingExit(JCTree tree, Type thrown) { 795 super(tree); 796 this.thrown = thrown; 797 } 798 } 799 800 @Override 801 void markDead() { 802 //do nothing 803 } 804 805 /*-------------------- Exceptions ----------------------*/ 806 807 /** Complain that pending exceptions are not caught. 808 */ 809 void errorUncaught() { 810 for (FlowPendingExit exit = pendingExits.next(); 811 exit != null; 812 exit = pendingExits.next()) { 813 if (classDef != null && 814 classDef.pos == exit.tree.pos) { 815 log.error(exit.tree.pos(), 816 Errors.UnreportedExceptionDefaultConstructor(exit.thrown)); 817 } else if (exit.tree.hasTag(VARDEF) && 818 ((JCVariableDecl)exit.tree).sym.isResourceVariable()) { 819 log.error(exit.tree.pos(), 820 Errors.UnreportedExceptionImplicitClose(exit.thrown, 821 ((JCVariableDecl)exit.tree).sym.name)); 822 } else { 823 log.error(exit.tree.pos(), 824 Errors.UnreportedExceptionNeedToCatchOrThrow(exit.thrown)); 825 } 826 } 827 } 828 829 /** Record that exception is potentially thrown and check that it 830 * is caught. 831 */ 832 void markThrown(JCTree tree, Type exc) { 833 if (!chk.isUnchecked(tree.pos(), exc)) { 834 if (!chk.isHandled(exc, caught)) { 835 pendingExits.append(new FlowPendingExit(tree, exc)); 836 } 837 thrown = chk.incl(exc, thrown); 838 } 839 } 840 841 /************************************************************************* 842 * Visitor methods for statements and definitions 843 *************************************************************************/ 844 845 /* ------------ Visitor methods for various sorts of trees -------------*/ 846 847 public void visitClassDef(JCClassDecl tree) { 848 if (tree.sym == null) return; 849 850 JCClassDecl classDefPrev = classDef; 851 List<Type> thrownPrev = thrown; 852 List<Type> caughtPrev = caught; 853 ListBuffer<FlowPendingExit> pendingExitsPrev = pendingExits; 854 Lint lintPrev = lint; 855 boolean anonymousClass = tree.name == names.empty; 856 pendingExits = new ListBuffer<>(); 857 if (!anonymousClass) { 858 caught = List.nil(); 859 } 860 classDef = tree; 861 thrown = List.nil(); 862 lint = lint.augment(tree.sym); 863 864 try { 865 // process all the static initializers 866 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 867 if (!l.head.hasTag(METHODDEF) && 868 (TreeInfo.flags(l.head) & STATIC) != 0) { 869 scan(l.head); 870 errorUncaught(); 871 } 872 } 873 874 // add intersection of all thrown clauses of initial constructors 875 // to set of caught exceptions, unless class is anonymous. 876 if (!anonymousClass) { 877 boolean firstConstructor = true; 878 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 879 if (TreeInfo.isInitialConstructor(l.head)) { 880 List<Type> mthrown = 881 ((JCMethodDecl) l.head).sym.type.getThrownTypes(); 882 if (firstConstructor) { 883 caught = mthrown; 884 firstConstructor = false; 885 } else { 886 caught = chk.intersect(mthrown, caught); 887 } 888 } 889 } 890 } 891 892 // process all the instance initializers 893 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 894 if (!l.head.hasTag(METHODDEF) && 895 (TreeInfo.flags(l.head) & STATIC) == 0) { 896 scan(l.head); 897 errorUncaught(); 898 } 899 } 900 901 // in an anonymous class, add the set of thrown exceptions to 902 // the throws clause of the synthetic constructor and propagate 903 // outwards. 904 // Changing the throws clause on the fly is okay here because 905 // the anonymous constructor can't be invoked anywhere else, 906 // and its type hasn't been cached. 907 if (anonymousClass) { 908 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 909 if (TreeInfo.isConstructor(l.head)) { 910 JCMethodDecl mdef = (JCMethodDecl)l.head; 911 scan(mdef); 912 mdef.thrown = make.Types(thrown); 913 mdef.sym.type = types.createMethodTypeWithThrown(mdef.sym.type, thrown); 914 } 915 } 916 thrownPrev = chk.union(thrown, thrownPrev); 917 } 918 919 // process all the methods 920 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 921 if (anonymousClass && TreeInfo.isConstructor(l.head)) 922 continue; // there can never be an uncaught exception. 923 if (l.head.hasTag(METHODDEF)) { 924 scan(l.head); 925 errorUncaught(); 926 } 927 } 928 929 thrown = thrownPrev; 930 } finally { 931 pendingExits = pendingExitsPrev; 932 caught = caughtPrev; 933 classDef = classDefPrev; 934 lint = lintPrev; 935 } 936 } 937 938 public void visitMethodDef(JCMethodDecl tree) { 939 if (tree.body == null) return; 940 941 List<Type> caughtPrev = caught; 942 List<Type> mthrown = tree.sym.type.getThrownTypes(); 943 Lint lintPrev = lint; 944 945 lint = lint.augment(tree.sym); 946 947 Assert.check(pendingExits.isEmpty()); 948 949 try { 950 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { 951 JCVariableDecl def = l.head; 952 scan(def); 953 } 954 if (TreeInfo.isInitialConstructor(tree)) 955 caught = chk.union(caught, mthrown); 956 else if ((tree.sym.flags() & (BLOCK | STATIC)) != BLOCK) 957 caught = mthrown; 958 // else we are in an instance initializer block; 959 // leave caught unchanged. 960 961 scan(tree.body); 962 963 List<FlowPendingExit> exits = pendingExits.toList(); 964 pendingExits = new ListBuffer<>(); 965 while (exits.nonEmpty()) { 966 FlowPendingExit exit = exits.head; 967 exits = exits.tail; 968 if (exit.thrown == null) { 969 Assert.check(exit.tree.hasTag(RETURN)); 970 } else { 971 // uncaught throws will be reported later 972 pendingExits.append(exit); 973 } 974 } 975 } finally { 976 caught = caughtPrev; 977 lint = lintPrev; 978 } 979 } 980 981 public void visitVarDef(JCVariableDecl tree) { 982 if (tree.init != null) { 983 Lint lintPrev = lint; 984 lint = lint.augment(tree.sym); 985 try{ 986 scan(tree.init); 987 } finally { 988 lint = lintPrev; 989 } 990 } 991 } 992 993 public void visitBlock(JCBlock tree) { 994 scan(tree.stats); 995 } 996 997 public void visitDoLoop(JCDoWhileLoop tree) { 998 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 999 pendingExits = new ListBuffer<>(); 1000 scan(tree.body); 1001 resolveContinues(tree); 1002 scan(tree.cond); 1003 resolveBreaks(tree, prevPendingExits); 1004 } 1005 1006 public void visitWhileLoop(JCWhileLoop tree) { 1007 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1008 pendingExits = new ListBuffer<>(); 1009 scan(tree.cond); 1010 scan(tree.body); 1011 resolveContinues(tree); 1012 resolveBreaks(tree, prevPendingExits); 1013 } 1014 1015 public void visitForLoop(JCForLoop tree) { 1016 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1017 scan(tree.init); 1018 pendingExits = new ListBuffer<>(); 1019 if (tree.cond != null) { 1020 scan(tree.cond); 1021 } 1022 scan(tree.body); 1023 resolveContinues(tree); 1024 scan(tree.step); 1025 resolveBreaks(tree, prevPendingExits); 1026 } 1027 1028 public void visitForeachLoop(JCEnhancedForLoop tree) { 1029 visitVarDef(tree.var); 1030 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1031 scan(tree.expr); 1032 pendingExits = new ListBuffer<>(); 1033 scan(tree.body); 1034 resolveContinues(tree); 1035 resolveBreaks(tree, prevPendingExits); 1036 } 1037 1038 public void visitLabelled(JCLabeledStatement tree) { 1039 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1040 pendingExits = new ListBuffer<>(); 1041 scan(tree.body); 1042 resolveBreaks(tree, prevPendingExits); 1043 } 1044 1045 public void visitSwitch(JCSwitch tree) { 1046 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1047 pendingExits = new ListBuffer<>(); 1048 scan(tree.selector); 1049 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { 1050 JCCase c = l.head; 1051 if (c.pat != null) { 1052 scan(c.pat); 1053 } 1054 scan(c.stats); 1055 } 1056 resolveBreaks(tree, prevPendingExits); 1057 } 1058 1059 public void visitTry(JCTry tree) { 1060 List<Type> caughtPrev = caught; 1061 List<Type> thrownPrev = thrown; 1062 thrown = List.nil(); 1063 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 1064 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ? 1065 ((JCTypeUnion)l.head.param.vartype).alternatives : 1066 List.of(l.head.param.vartype); 1067 for (JCExpression ct : subClauses) { 1068 caught = chk.incl(ct.type, caught); 1069 } 1070 } 1071 1072 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1073 pendingExits = new ListBuffer<>(); 1074 for (JCTree resource : tree.resources) { 1075 if (resource instanceof JCVariableDecl) { 1076 JCVariableDecl vdecl = (JCVariableDecl) resource; 1077 visitVarDef(vdecl); 1078 } else if (resource instanceof JCExpression) { 1079 scan((JCExpression) resource); 1080 } else { 1081 throw new AssertionError(tree); // parser error 1082 } 1083 } 1084 for (JCTree resource : tree.resources) { 1085 List<Type> closeableSupertypes = resource.type.isCompound() ? 1086 types.interfaces(resource.type).prepend(types.supertype(resource.type)) : 1087 List.of(resource.type); 1088 for (Type sup : closeableSupertypes) { 1089 if (types.asSuper(sup, syms.autoCloseableType.tsym) != null) { 1090 Symbol closeMethod = rs.resolveQualifiedMethod(tree, 1091 attrEnv, 1092 types.skipTypeVars(sup, false), 1093 names.close, 1094 List.nil(), 1095 List.nil()); 1096 Type mt = types.memberType(resource.type, closeMethod); 1097 if (closeMethod.kind == MTH) { 1098 for (Type t : mt.getThrownTypes()) { 1099 markThrown(resource, t); 1100 } 1101 } 1102 } 1103 } 1104 } 1105 scan(tree.body); 1106 List<Type> thrownInTry = allowImprovedCatchAnalysis ? 1107 chk.union(thrown, List.of(syms.runtimeExceptionType, syms.errorType)) : 1108 thrown; 1109 thrown = thrownPrev; 1110 caught = caughtPrev; 1111 1112 List<Type> caughtInTry = List.nil(); 1113 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 1114 JCVariableDecl param = l.head.param; 1115 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ? 1116 ((JCTypeUnion)l.head.param.vartype).alternatives : 1117 List.of(l.head.param.vartype); 1118 List<Type> ctypes = List.nil(); 1119 List<Type> rethrownTypes = chk.diff(thrownInTry, caughtInTry); 1120 for (JCExpression ct : subClauses) { 1121 Type exc = ct.type; 1122 if (exc != syms.unknownType) { 1123 ctypes = ctypes.append(exc); 1124 if (types.isSameType(exc, syms.objectType)) 1125 continue; 1126 checkCaughtType(l.head.pos(), exc, thrownInTry, caughtInTry); 1127 caughtInTry = chk.incl(exc, caughtInTry); 1128 } 1129 } 1130 scan(param); 1131 preciseRethrowTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes)); 1132 scan(l.head.body); 1133 preciseRethrowTypes.remove(param.sym); 1134 } 1135 if (tree.finalizer != null) { 1136 List<Type> savedThrown = thrown; 1137 thrown = List.nil(); 1138 ListBuffer<FlowPendingExit> exits = pendingExits; 1139 pendingExits = prevPendingExits; 1140 scan(tree.finalizer); 1141 if (!tree.finallyCanCompleteNormally) { 1142 // discard exits and exceptions from try and finally 1143 thrown = chk.union(thrown, thrownPrev); 1144 } else { 1145 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); 1146 thrown = chk.union(thrown, savedThrown); 1147 // FIX: this doesn't preserve source order of exits in catch 1148 // versus finally! 1149 while (exits.nonEmpty()) { 1150 pendingExits.append(exits.next()); 1151 } 1152 } 1153 } else { 1154 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); 1155 ListBuffer<FlowPendingExit> exits = pendingExits; 1156 pendingExits = prevPendingExits; 1157 while (exits.nonEmpty()) pendingExits.append(exits.next()); 1158 } 1159 } 1160 1161 @Override 1162 public void visitIf(JCIf tree) { 1163 scan(tree.cond); 1164 scan(tree.thenpart); 1165 if (tree.elsepart != null) { 1166 scan(tree.elsepart); 1167 } 1168 } 1169 1170 void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) { 1171 if (chk.subset(exc, caughtInTry)) { 1172 log.error(pos, Errors.ExceptAlreadyCaught(exc)); 1173 } else if (!chk.isUnchecked(pos, exc) && 1174 !isExceptionOrThrowable(exc) && 1175 !chk.intersects(exc, thrownInTry)) { 1176 log.error(pos, Errors.ExceptNeverThrownInTry(exc)); 1177 } else if (allowImprovedCatchAnalysis) { 1178 List<Type> catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry); 1179 // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an 1180 // unchecked exception, the result list would not be empty, as the augmented 1181 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked 1182 // exception, that would have been covered in the branch above 1183 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() && 1184 !isExceptionOrThrowable(exc)) { 1185 String key = catchableThrownTypes.length() == 1 ? 1186 "unreachable.catch" : 1187 "unreachable.catch.1"; 1188 log.warning(pos, key, catchableThrownTypes); 1189 } 1190 } 1191 } 1192 //where 1193 private boolean isExceptionOrThrowable(Type exc) { 1194 return exc.tsym == syms.throwableType.tsym || 1195 exc.tsym == syms.exceptionType.tsym; 1196 } 1197 1198 public void visitBreak(JCBreak tree) { 1199 recordExit(new FlowPendingExit(tree, null)); 1200 } 1201 1202 public void visitContinue(JCContinue tree) { 1203 recordExit(new FlowPendingExit(tree, null)); 1204 } 1205 1206 public void visitReturn(JCReturn tree) { 1207 scan(tree.expr); 1208 recordExit(new FlowPendingExit(tree, null)); 1209 } 1210 1211 public void visitThrow(JCThrow tree) { 1212 scan(tree.expr); 1213 Symbol sym = TreeInfo.symbol(tree.expr); 1214 if (sym != null && 1215 sym.kind == VAR && 1216 (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 && 1217 preciseRethrowTypes.get(sym) != null && 1218 allowImprovedRethrowAnalysis) { 1219 for (Type t : preciseRethrowTypes.get(sym)) { 1220 markThrown(tree, t); 1221 } 1222 } 1223 else { 1224 markThrown(tree, tree.expr.type); 1225 } 1226 markDead(); 1227 } 1228 1229 public void visitApply(JCMethodInvocation tree) { 1230 scan(tree.meth); 1231 scan(tree.args); 1232 for (List<Type> l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail) 1233 markThrown(tree, l.head); 1234 } 1235 1236 public void visitNewClass(JCNewClass tree) { 1237 scan(tree.encl); 1238 scan(tree.args); 1239 // scan(tree.def); 1240 for (List<Type> l = tree.constructorType.getThrownTypes(); 1241 l.nonEmpty(); 1242 l = l.tail) { 1243 markThrown(tree, l.head); 1244 } 1245 List<Type> caughtPrev = caught; 1246 try { 1247 // If the new class expression defines an anonymous class, 1248 // analysis of the anonymous constructor may encounter thrown 1249 // types which are unsubstituted type variables. 1250 // However, since the constructor's actual thrown types have 1251 // already been marked as thrown, it is safe to simply include 1252 // each of the constructor's formal thrown types in the set of 1253 // 'caught/declared to be thrown' types, for the duration of 1254 // the class def analysis. 1255 if (tree.def != null) 1256 for (List<Type> l = tree.constructor.type.getThrownTypes(); 1257 l.nonEmpty(); 1258 l = l.tail) { 1259 caught = chk.incl(l.head, caught); 1260 } 1261 scan(tree.def); 1262 } 1263 finally { 1264 caught = caughtPrev; 1265 } 1266 } 1267 1268 @Override 1269 public void visitLambda(JCLambda tree) { 1270 if (tree.type != null && 1271 tree.type.isErroneous()) { 1272 return; 1273 } 1274 List<Type> prevCaught = caught; 1275 List<Type> prevThrown = thrown; 1276 ListBuffer<FlowPendingExit> prevPending = pendingExits; 1277 try { 1278 pendingExits = new ListBuffer<>(); 1279 caught = tree.getDescriptorType(types).getThrownTypes(); 1280 thrown = List.nil(); 1281 scan(tree.body); 1282 List<FlowPendingExit> exits = pendingExits.toList(); 1283 pendingExits = new ListBuffer<>(); 1284 while (exits.nonEmpty()) { 1285 FlowPendingExit exit = exits.head; 1286 exits = exits.tail; 1287 if (exit.thrown == null) { 1288 Assert.check(exit.tree.hasTag(RETURN)); 1289 } else { 1290 // uncaught throws will be reported later 1291 pendingExits.append(exit); 1292 } 1293 } 1294 1295 errorUncaught(); 1296 } finally { 1297 pendingExits = prevPending; 1298 caught = prevCaught; 1299 thrown = prevThrown; 1300 } 1301 } 1302 1303 public void visitModuleDef(JCModuleDecl tree) { 1304 // Do nothing for modules 1305 } 1306 1307 /************************************************************************** 1308 * main method 1309 *************************************************************************/ 1310 1311 /** Perform definite assignment/unassignment analysis on a tree. 1312 */ 1313 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 1314 analyzeTree(env, env.tree, make); 1315 } 1316 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) { 1317 try { 1318 attrEnv = env; 1319 Flow.this.make = make; 1320 pendingExits = new ListBuffer<>(); 1321 preciseRethrowTypes = new HashMap<>(); 1322 this.thrown = this.caught = null; 1323 this.classDef = null; 1324 scan(tree); 1325 } finally { 1326 pendingExits = null; 1327 Flow.this.make = null; 1328 this.thrown = this.caught = null; 1329 this.classDef = null; 1330 } 1331 } 1332 } 1333 1334 /** 1335 * Specialized pass that performs reachability analysis on a lambda 1336 */ 1337 class LambdaAliveAnalyzer extends AliveAnalyzer { 1338 1339 boolean inLambda; 1340 1341 @Override 1342 public void visitReturn(JCReturn tree) { 1343 //ignore lambda return expression (which might not even be attributed) 1344 recordExit(new PendingExit(tree)); 1345 } 1346 1347 @Override 1348 public void visitLambda(JCLambda tree) { 1349 if (inLambda || tree.getBodyKind() == BodyKind.EXPRESSION) { 1350 return; 1351 } 1352 inLambda = true; 1353 try { 1354 super.visitLambda(tree); 1355 } finally { 1356 inLambda = false; 1357 } 1358 } 1359 1360 @Override 1361 public void visitClassDef(JCClassDecl tree) { 1362 //skip 1363 } 1364 } 1365 1366 /** 1367 * Specialized pass that performs DA/DU on a lambda 1368 */ 1369 class LambdaAssignAnalyzer extends AssignAnalyzer { 1370 WriteableScope enclosedSymbols; 1371 boolean inLambda; 1372 1373 LambdaAssignAnalyzer(Env<AttrContext> env) { 1374 enclosedSymbols = WriteableScope.create(env.enclClass.sym); 1375 } 1376 1377 @Override 1378 public void visitLambda(JCLambda tree) { 1379 if (inLambda) { 1380 return; 1381 } 1382 inLambda = true; 1383 try { 1384 super.visitLambda(tree); 1385 } finally { 1386 inLambda = false; 1387 } 1388 } 1389 1390 @Override 1391 public void visitVarDef(JCVariableDecl tree) { 1392 enclosedSymbols.enter(tree.sym); 1393 super.visitVarDef(tree); 1394 } 1395 @Override 1396 protected boolean trackable(VarSymbol sym) { 1397 return enclosedSymbols.includes(sym) && 1398 sym.owner.kind == MTH; 1399 } 1400 1401 @Override 1402 public void visitClassDef(JCClassDecl tree) { 1403 //skip 1404 } 1405 } 1406 1407 /** 1408 * Specialized pass that performs inference of thrown types for lambdas. 1409 */ 1410 class LambdaFlowAnalyzer extends FlowAnalyzer { 1411 List<Type> inferredThrownTypes; 1412 boolean inLambda; 1413 @Override 1414 public void visitLambda(JCLambda tree) { 1415 if ((tree.type != null && 1416 tree.type.isErroneous()) || inLambda) { 1417 return; 1418 } 1419 List<Type> prevCaught = caught; 1420 List<Type> prevThrown = thrown; 1421 ListBuffer<FlowPendingExit> prevPending = pendingExits; 1422 inLambda = true; 1423 try { 1424 pendingExits = new ListBuffer<>(); 1425 caught = List.of(syms.throwableType); 1426 thrown = List.nil(); 1427 scan(tree.body); 1428 inferredThrownTypes = thrown; 1429 } finally { 1430 pendingExits = prevPending; 1431 caught = prevCaught; 1432 thrown = prevThrown; 1433 inLambda = false; 1434 } 1435 } 1436 @Override 1437 public void visitClassDef(JCClassDecl tree) { 1438 //skip 1439 } 1440 } 1441 1442 /** 1443 * This pass implements (i) definite assignment analysis, which ensures that 1444 * each variable is assigned when used and (ii) definite unassignment analysis, 1445 * which ensures that no final variable is assigned more than once. This visitor 1446 * depends on the results of the liveliness analyzer. This pass is also used to mark 1447 * effectively-final local variables/parameters. 1448 */ 1449 1450 public class AssignAnalyzer extends BaseAnalyzer<AssignAnalyzer.AssignPendingExit> { 1451 1452 /** The set of definitely assigned variables. 1453 */ 1454 final Bits inits; 1455 1456 /** The set of definitely unassigned variables. 1457 */ 1458 final Bits uninits; 1459 1460 /** The set of variables that are definitely unassigned everywhere 1461 * in current try block. This variable is maintained lazily; it is 1462 * updated only when something gets removed from uninits, 1463 * typically by being assigned in reachable code. To obtain the 1464 * correct set of variables which are definitely unassigned 1465 * anywhere in current try block, intersect uninitsTry and 1466 * uninits. 1467 */ 1468 final Bits uninitsTry; 1469 1470 /** When analyzing a condition, inits and uninits are null. 1471 * Instead we have: 1472 */ 1473 final Bits initsWhenTrue; 1474 final Bits initsWhenFalse; 1475 final Bits uninitsWhenTrue; 1476 final Bits uninitsWhenFalse; 1477 1478 /** A mapping from addresses to variable symbols. 1479 */ 1480 protected JCVariableDecl[] vardecls; 1481 1482 /** The current class being defined. 1483 */ 1484 JCClassDecl classDef; 1485 1486 /** The first variable sequence number in this class definition. 1487 */ 1488 int firstadr; 1489 1490 /** The next available variable sequence number. 1491 */ 1492 protected int nextadr; 1493 1494 /** The first variable sequence number in a block that can return. 1495 */ 1496 protected int returnadr; 1497 1498 /** The list of unreferenced automatic resources. 1499 */ 1500 WriteableScope unrefdResources; 1501 1502 /** Modified when processing a loop body the second time for DU analysis. */ 1503 FlowKind flowKind = FlowKind.NORMAL; 1504 1505 /** The starting position of the analyzed tree */ 1506 int startPos; 1507 1508 public class AssignPendingExit extends BaseAnalyzer.PendingExit { 1509 1510 final Bits inits; 1511 final Bits uninits; 1512 final Bits exit_inits = new Bits(true); 1513 final Bits exit_uninits = new Bits(true); 1514 1515 public AssignPendingExit(JCTree tree, final Bits inits, final Bits uninits) { 1516 super(tree); 1517 this.inits = inits; 1518 this.uninits = uninits; 1519 this.exit_inits.assign(inits); 1520 this.exit_uninits.assign(uninits); 1521 } 1522 1523 @Override 1524 public void resolveJump() { 1525 inits.andSet(exit_inits); 1526 uninits.andSet(exit_uninits); 1527 } 1528 } 1529 1530 public AssignAnalyzer() { 1531 this.inits = new Bits(); 1532 uninits = new Bits(); 1533 uninitsTry = new Bits(); 1534 initsWhenTrue = new Bits(true); 1535 initsWhenFalse = new Bits(true); 1536 uninitsWhenTrue = new Bits(true); 1537 uninitsWhenFalse = new Bits(true); 1538 } 1539 1540 private boolean isInitialConstructor = false; 1541 1542 @Override 1543 protected void markDead() { 1544 if (!isInitialConstructor) { 1545 inits.inclRange(returnadr, nextadr); 1546 } else { 1547 for (int address = returnadr; address < nextadr; address++) { 1548 if (!(isFinalUninitializedStaticField(vardecls[address].sym))) { 1549 inits.incl(address); 1550 } 1551 } 1552 } 1553 uninits.inclRange(returnadr, nextadr); 1554 } 1555 1556 /*-------------- Processing variables ----------------------*/ 1557 1558 /** Do we need to track init/uninit state of this symbol? 1559 * I.e. is symbol either a local or a blank final variable? 1560 */ 1561 protected boolean trackable(VarSymbol sym) { 1562 return 1563 sym.pos >= startPos && 1564 ((sym.owner.kind == MTH || 1565 isFinalUninitializedField(sym))); 1566 } 1567 1568 boolean isFinalUninitializedField(VarSymbol sym) { 1569 return sym.owner.kind == TYP && 1570 ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL && 1571 classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)); 1572 } 1573 1574 boolean isFinalUninitializedStaticField(VarSymbol sym) { 1575 return isFinalUninitializedField(sym) && sym.isStatic(); 1576 } 1577 1578 /** Initialize new trackable variable by setting its address field 1579 * to the next available sequence number and entering it under that 1580 * index into the vars array. 1581 */ 1582 void newVar(JCVariableDecl varDecl) { 1583 VarSymbol sym = varDecl.sym; 1584 vardecls = ArrayUtils.ensureCapacity(vardecls, nextadr); 1585 if ((sym.flags() & FINAL) == 0) { 1586 sym.flags_field |= EFFECTIVELY_FINAL; 1587 } 1588 sym.adr = nextadr; 1589 vardecls[nextadr] = varDecl; 1590 inits.excl(nextadr); 1591 uninits.incl(nextadr); 1592 nextadr++; 1593 } 1594 1595 /** Record an initialization of a trackable variable. 1596 */ 1597 void letInit(DiagnosticPosition pos, VarSymbol sym) { 1598 if (sym.adr >= firstadr && trackable(sym)) { 1599 if ((sym.flags() & EFFECTIVELY_FINAL) != 0) { 1600 if (!uninits.isMember(sym.adr)) { 1601 //assignment targeting an effectively final variable 1602 //makes the variable lose its status of effectively final 1603 //if the variable is _not_ definitively unassigned 1604 sym.flags_field &= ~EFFECTIVELY_FINAL; 1605 } else { 1606 uninit(sym); 1607 } 1608 } 1609 else if ((sym.flags() & FINAL) != 0) { 1610 if ((sym.flags() & PARAMETER) != 0) { 1611 if ((sym.flags() & UNION) != 0) { //multi-catch parameter 1612 log.error(pos, Errors.MulticatchParameterMayNotBeAssigned(sym)); 1613 } 1614 else { 1615 log.error(pos, 1616 Errors.FinalParameterMayNotBeAssigned(sym)); 1617 } 1618 } else if (!uninits.isMember(sym.adr)) { 1619 log.error(pos, flowKind.errKey, sym); 1620 } else { 1621 uninit(sym); 1622 } 1623 } 1624 inits.incl(sym.adr); 1625 } else if ((sym.flags() & FINAL) != 0) { 1626 log.error(pos, Errors.VarMightAlreadyBeAssigned(sym)); 1627 } 1628 } 1629 //where 1630 void uninit(VarSymbol sym) { 1631 if (!inits.isMember(sym.adr)) { 1632 // reachable assignment 1633 uninits.excl(sym.adr); 1634 uninitsTry.excl(sym.adr); 1635 } else { 1636 //log.rawWarning(pos, "unreachable assignment");//DEBUG 1637 uninits.excl(sym.adr); 1638 } 1639 } 1640 1641 /** If tree is either a simple name or of the form this.name or 1642 * C.this.name, and tree represents a trackable variable, 1643 * record an initialization of the variable. 1644 */ 1645 void letInit(JCTree tree) { 1646 tree = TreeInfo.skipParens(tree); 1647 if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { 1648 Symbol sym = TreeInfo.symbol(tree); 1649 if (sym.kind == VAR) { 1650 letInit(tree.pos(), (VarSymbol)sym); 1651 } 1652 } 1653 } 1654 1655 /** Check that trackable variable is initialized. 1656 */ 1657 void checkInit(DiagnosticPosition pos, VarSymbol sym) { 1658 checkInit(pos, sym, "var.might.not.have.been.initialized"); 1659 } 1660 1661 void checkInit(DiagnosticPosition pos, VarSymbol sym, String errkey) { 1662 if ((sym.adr >= firstadr || sym.owner.kind != TYP) && 1663 trackable(sym) && 1664 !inits.isMember(sym.adr)) { 1665 log.error(pos, errkey, sym); 1666 inits.incl(sym.adr); 1667 } 1668 } 1669 1670 /** Utility method to reset several Bits instances. 1671 */ 1672 private void resetBits(Bits... bits) { 1673 for (Bits b : bits) { 1674 b.reset(); 1675 } 1676 } 1677 1678 /** Split (duplicate) inits/uninits into WhenTrue/WhenFalse sets 1679 */ 1680 void split(boolean setToNull) { 1681 initsWhenFalse.assign(inits); 1682 uninitsWhenFalse.assign(uninits); 1683 initsWhenTrue.assign(inits); 1684 uninitsWhenTrue.assign(uninits); 1685 if (setToNull) { 1686 resetBits(inits, uninits); 1687 } 1688 } 1689 1690 /** Merge (intersect) inits/uninits from WhenTrue/WhenFalse sets. 1691 */ 1692 protected void merge() { 1693 inits.assign(initsWhenFalse.andSet(initsWhenTrue)); 1694 uninits.assign(uninitsWhenFalse.andSet(uninitsWhenTrue)); 1695 } 1696 1697 /* ************************************************************************ 1698 * Visitor methods for statements and definitions 1699 *************************************************************************/ 1700 1701 /** Analyze an expression. Make sure to set (un)inits rather than 1702 * (un)initsWhenTrue(WhenFalse) on exit. 1703 */ 1704 void scanExpr(JCTree tree) { 1705 if (tree != null) { 1706 scan(tree); 1707 if (inits.isReset()) { 1708 merge(); 1709 } 1710 } 1711 } 1712 1713 /** Analyze a list of expressions. 1714 */ 1715 void scanExprs(List<? extends JCExpression> trees) { 1716 if (trees != null) 1717 for (List<? extends JCExpression> l = trees; l.nonEmpty(); l = l.tail) 1718 scanExpr(l.head); 1719 } 1720 1721 /** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse) 1722 * rather than (un)inits on exit. 1723 */ 1724 void scanCond(JCTree tree) { 1725 if (tree.type.isFalse()) { 1726 if (inits.isReset()) merge(); 1727 initsWhenTrue.assign(inits); 1728 initsWhenTrue.inclRange(firstadr, nextadr); 1729 uninitsWhenTrue.assign(uninits); 1730 uninitsWhenTrue.inclRange(firstadr, nextadr); 1731 initsWhenFalse.assign(inits); 1732 uninitsWhenFalse.assign(uninits); 1733 } else if (tree.type.isTrue()) { 1734 if (inits.isReset()) merge(); 1735 initsWhenFalse.assign(inits); 1736 initsWhenFalse.inclRange(firstadr, nextadr); 1737 uninitsWhenFalse.assign(uninits); 1738 uninitsWhenFalse.inclRange(firstadr, nextadr); 1739 initsWhenTrue.assign(inits); 1740 uninitsWhenTrue.assign(uninits); 1741 } else { 1742 scan(tree); 1743 if (!inits.isReset()) 1744 split(tree.type != syms.unknownType); 1745 } 1746 if (tree.type != syms.unknownType) { 1747 resetBits(inits, uninits); 1748 } 1749 } 1750 1751 /* ------------ Visitor methods for various sorts of trees -------------*/ 1752 1753 public void visitClassDef(JCClassDecl tree) { 1754 if (tree.sym == null) { 1755 return; 1756 } 1757 1758 Lint lintPrev = lint; 1759 lint = lint.augment(tree.sym); 1760 try { 1761 if (tree.sym == null) { 1762 return; 1763 } 1764 1765 JCClassDecl classDefPrev = classDef; 1766 int firstadrPrev = firstadr; 1767 int nextadrPrev = nextadr; 1768 ListBuffer<AssignPendingExit> pendingExitsPrev = pendingExits; 1769 1770 pendingExits = new ListBuffer<>(); 1771 if (tree.name != names.empty) { 1772 firstadr = nextadr; 1773 } 1774 classDef = tree; 1775 try { 1776 // define all the static fields 1777 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1778 if (l.head.hasTag(VARDEF)) { 1779 JCVariableDecl def = (JCVariableDecl)l.head; 1780 if ((def.mods.flags & STATIC) != 0) { 1781 VarSymbol sym = def.sym; 1782 if (trackable(sym)) { 1783 newVar(def); 1784 } 1785 } 1786 } 1787 } 1788 1789 // process all the static initializers 1790 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1791 if (!l.head.hasTag(METHODDEF) && 1792 (TreeInfo.flags(l.head) & STATIC) != 0) { 1793 scan(l.head); 1794 } 1795 } 1796 1797 // define all the instance fields 1798 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1799 if (l.head.hasTag(VARDEF)) { 1800 JCVariableDecl def = (JCVariableDecl)l.head; 1801 if ((def.mods.flags & STATIC) == 0) { 1802 VarSymbol sym = def.sym; 1803 if (trackable(sym)) { 1804 newVar(def); 1805 } 1806 } 1807 } 1808 } 1809 1810 // process all the instance initializers 1811 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1812 if (!l.head.hasTag(METHODDEF) && 1813 (TreeInfo.flags(l.head) & STATIC) == 0) { 1814 scan(l.head); 1815 } 1816 } 1817 1818 // process all the methods 1819 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1820 if (l.head.hasTag(METHODDEF)) { 1821 scan(l.head); 1822 } 1823 } 1824 } finally { 1825 pendingExits = pendingExitsPrev; 1826 nextadr = nextadrPrev; 1827 firstadr = firstadrPrev; 1828 classDef = classDefPrev; 1829 } 1830 } finally { 1831 lint = lintPrev; 1832 } 1833 } 1834 1835 public void visitMethodDef(JCMethodDecl tree) { 1836 if (tree.body == null) { 1837 return; 1838 } 1839 1840 /* MemberEnter can generate synthetic methods ignore them 1841 */ 1842 if ((tree.sym.flags() & SYNTHETIC) != 0) { 1843 return; 1844 } 1845 1846 Lint lintPrev = lint; 1847 lint = lint.augment(tree.sym); 1848 try { 1849 if (tree.body == null) { 1850 return; 1851 } 1852 /* Ignore synthetic methods, except for translated lambda methods. 1853 */ 1854 if ((tree.sym.flags() & (SYNTHETIC | LAMBDA_METHOD)) == SYNTHETIC) { 1855 return; 1856 } 1857 1858 final Bits initsPrev = new Bits(inits); 1859 final Bits uninitsPrev = new Bits(uninits); 1860 int nextadrPrev = nextadr; 1861 int firstadrPrev = firstadr; 1862 int returnadrPrev = returnadr; 1863 1864 Assert.check(pendingExits.isEmpty()); 1865 boolean lastInitialConstructor = isInitialConstructor; 1866 try { 1867 isInitialConstructor = TreeInfo.isInitialConstructor(tree); 1868 1869 if (!isInitialConstructor) { 1870 firstadr = nextadr; 1871 } 1872 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { 1873 JCVariableDecl def = l.head; 1874 scan(def); 1875 Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter without PARAMETER flag"); 1876 /* If we are executing the code from Gen, then there can be 1877 * synthetic or mandated variables, ignore them. 1878 */ 1879 initParam(def); 1880 } 1881 // else we are in an instance initializer block; 1882 // leave caught unchanged. 1883 scan(tree.body); 1884 1885 if (isInitialConstructor) { 1886 boolean isSynthesized = (tree.sym.flags() & 1887 GENERATEDCONSTR) != 0; 1888 for (int i = firstadr; i < nextadr; i++) { 1889 JCVariableDecl vardecl = vardecls[i]; 1890 VarSymbol var = vardecl.sym; 1891 if (var.owner == classDef.sym) { 1892 // choose the diagnostic position based on whether 1893 // the ctor is default(synthesized) or not 1894 if (isSynthesized) { 1895 checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), 1896 var, "var.not.initialized.in.default.constructor"); 1897 } else { 1898 checkInit(TreeInfo.diagEndPos(tree.body), var); 1899 } 1900 } 1901 } 1902 } 1903 List<AssignPendingExit> exits = pendingExits.toList(); 1904 pendingExits = new ListBuffer<>(); 1905 while (exits.nonEmpty()) { 1906 AssignPendingExit exit = exits.head; 1907 exits = exits.tail; 1908 Assert.check(exit.tree.hasTag(RETURN), exit.tree); 1909 if (isInitialConstructor) { 1910 inits.assign(exit.exit_inits); 1911 for (int i = firstadr; i < nextadr; i++) { 1912 checkInit(exit.tree.pos(), vardecls[i].sym); 1913 } 1914 } 1915 } 1916 } finally { 1917 inits.assign(initsPrev); 1918 uninits.assign(uninitsPrev); 1919 nextadr = nextadrPrev; 1920 firstadr = firstadrPrev; 1921 returnadr = returnadrPrev; 1922 isInitialConstructor = lastInitialConstructor; 1923 } 1924 } finally { 1925 lint = lintPrev; 1926 } 1927 } 1928 1929 protected void initParam(JCVariableDecl def) { 1930 inits.incl(def.sym.adr); 1931 uninits.excl(def.sym.adr); 1932 } 1933 1934 public void visitVarDef(JCVariableDecl tree) { 1935 Lint lintPrev = lint; 1936 lint = lint.augment(tree.sym); 1937 try{ 1938 boolean track = trackable(tree.sym); 1939 if (track && tree.sym.owner.kind == MTH) { 1940 newVar(tree); 1941 } 1942 if (tree.init != null) { 1943 scanExpr(tree.init); 1944 if (track) { 1945 letInit(tree.pos(), tree.sym); 1946 } 1947 } 1948 } finally { 1949 lint = lintPrev; 1950 } 1951 } 1952 1953 public void visitBlock(JCBlock tree) { 1954 int nextadrPrev = nextadr; 1955 scan(tree.stats); 1956 nextadr = nextadrPrev; 1957 } 1958 1959 public void visitDoLoop(JCDoWhileLoop tree) { 1960 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 1961 FlowKind prevFlowKind = flowKind; 1962 flowKind = FlowKind.NORMAL; 1963 final Bits initsSkip = new Bits(true); 1964 final Bits uninitsSkip = new Bits(true); 1965 pendingExits = new ListBuffer<>(); 1966 int prevErrors = log.nerrors; 1967 do { 1968 final Bits uninitsEntry = new Bits(uninits); 1969 uninitsEntry.excludeFrom(nextadr); 1970 scan(tree.body); 1971 resolveContinues(tree); 1972 scanCond(tree.cond); 1973 if (!flowKind.isFinal()) { 1974 initsSkip.assign(initsWhenFalse); 1975 uninitsSkip.assign(uninitsWhenFalse); 1976 } 1977 if (log.nerrors != prevErrors || 1978 flowKind.isFinal() || 1979 new Bits(uninitsEntry).diffSet(uninitsWhenTrue).nextBit(firstadr)==-1) 1980 break; 1981 inits.assign(initsWhenTrue); 1982 uninits.assign(uninitsEntry.andSet(uninitsWhenTrue)); 1983 flowKind = FlowKind.SPECULATIVE_LOOP; 1984 } while (true); 1985 flowKind = prevFlowKind; 1986 inits.assign(initsSkip); 1987 uninits.assign(uninitsSkip); 1988 resolveBreaks(tree, prevPendingExits); 1989 } 1990 1991 public void visitWhileLoop(JCWhileLoop tree) { 1992 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 1993 FlowKind prevFlowKind = flowKind; 1994 flowKind = FlowKind.NORMAL; 1995 final Bits initsSkip = new Bits(true); 1996 final Bits uninitsSkip = new Bits(true); 1997 pendingExits = new ListBuffer<>(); 1998 int prevErrors = log.nerrors; 1999 final Bits uninitsEntry = new Bits(uninits); 2000 uninitsEntry.excludeFrom(nextadr); 2001 do { 2002 scanCond(tree.cond); 2003 if (!flowKind.isFinal()) { 2004 initsSkip.assign(initsWhenFalse) ; 2005 uninitsSkip.assign(uninitsWhenFalse); 2006 } 2007 inits.assign(initsWhenTrue); 2008 uninits.assign(uninitsWhenTrue); 2009 scan(tree.body); 2010 resolveContinues(tree); 2011 if (log.nerrors != prevErrors || 2012 flowKind.isFinal() || 2013 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) { 2014 break; 2015 } 2016 uninits.assign(uninitsEntry.andSet(uninits)); 2017 flowKind = FlowKind.SPECULATIVE_LOOP; 2018 } while (true); 2019 flowKind = prevFlowKind; 2020 //a variable is DA/DU after the while statement, if it's DA/DU assuming the 2021 //branch is not taken AND if it's DA/DU before any break statement 2022 inits.assign(initsSkip); 2023 uninits.assign(uninitsSkip); 2024 resolveBreaks(tree, prevPendingExits); 2025 } 2026 2027 public void visitForLoop(JCForLoop tree) { 2028 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 2029 FlowKind prevFlowKind = flowKind; 2030 flowKind = FlowKind.NORMAL; 2031 int nextadrPrev = nextadr; 2032 scan(tree.init); 2033 final Bits initsSkip = new Bits(true); 2034 final Bits uninitsSkip = new Bits(true); 2035 pendingExits = new ListBuffer<>(); 2036 int prevErrors = log.nerrors; 2037 do { 2038 final Bits uninitsEntry = new Bits(uninits); 2039 uninitsEntry.excludeFrom(nextadr); 2040 if (tree.cond != null) { 2041 scanCond(tree.cond); 2042 if (!flowKind.isFinal()) { 2043 initsSkip.assign(initsWhenFalse); 2044 uninitsSkip.assign(uninitsWhenFalse); 2045 } 2046 inits.assign(initsWhenTrue); 2047 uninits.assign(uninitsWhenTrue); 2048 } else if (!flowKind.isFinal()) { 2049 initsSkip.assign(inits); 2050 initsSkip.inclRange(firstadr, nextadr); 2051 uninitsSkip.assign(uninits); 2052 uninitsSkip.inclRange(firstadr, nextadr); 2053 } 2054 scan(tree.body); 2055 resolveContinues(tree); 2056 scan(tree.step); 2057 if (log.nerrors != prevErrors || 2058 flowKind.isFinal() || 2059 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) 2060 break; 2061 uninits.assign(uninitsEntry.andSet(uninits)); 2062 flowKind = FlowKind.SPECULATIVE_LOOP; 2063 } while (true); 2064 flowKind = prevFlowKind; 2065 //a variable is DA/DU after a for loop, if it's DA/DU assuming the 2066 //branch is not taken AND if it's DA/DU before any break statement 2067 inits.assign(initsSkip); 2068 uninits.assign(uninitsSkip); 2069 resolveBreaks(tree, prevPendingExits); 2070 nextadr = nextadrPrev; 2071 } 2072 2073 public void visitForeachLoop(JCEnhancedForLoop tree) { 2074 visitVarDef(tree.var); 2075 2076 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 2077 FlowKind prevFlowKind = flowKind; 2078 flowKind = FlowKind.NORMAL; 2079 int nextadrPrev = nextadr; 2080 scan(tree.expr); 2081 final Bits initsStart = new Bits(inits); 2082 final Bits uninitsStart = new Bits(uninits); 2083 2084 letInit(tree.pos(), tree.var.sym); 2085 pendingExits = new ListBuffer<>(); 2086 int prevErrors = log.nerrors; 2087 do { 2088 final Bits uninitsEntry = new Bits(uninits); 2089 uninitsEntry.excludeFrom(nextadr); 2090 scan(tree.body); 2091 resolveContinues(tree); 2092 if (log.nerrors != prevErrors || 2093 flowKind.isFinal() || 2094 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) 2095 break; 2096 uninits.assign(uninitsEntry.andSet(uninits)); 2097 flowKind = FlowKind.SPECULATIVE_LOOP; 2098 } while (true); 2099 flowKind = prevFlowKind; 2100 inits.assign(initsStart); 2101 uninits.assign(uninitsStart.andSet(uninits)); 2102 resolveBreaks(tree, prevPendingExits); 2103 nextadr = nextadrPrev; 2104 } 2105 2106 public void visitLabelled(JCLabeledStatement tree) { 2107 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 2108 pendingExits = new ListBuffer<>(); 2109 scan(tree.body); 2110 resolveBreaks(tree, prevPendingExits); 2111 } 2112 2113 public void visitSwitch(JCSwitch tree) { 2114 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 2115 pendingExits = new ListBuffer<>(); 2116 int nextadrPrev = nextadr; 2117 scanExpr(tree.selector); 2118 final Bits initsSwitch = new Bits(inits); 2119 final Bits uninitsSwitch = new Bits(uninits); 2120 boolean hasDefault = false; 2121 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { 2122 inits.assign(initsSwitch); 2123 uninits.assign(uninits.andSet(uninitsSwitch)); 2124 JCCase c = l.head; 2125 if (c.pat == null) { 2126 hasDefault = true; 2127 } else { 2128 scanExpr(c.pat); 2129 } 2130 if (hasDefault) { 2131 inits.assign(initsSwitch); 2132 uninits.assign(uninits.andSet(uninitsSwitch)); 2133 } 2134 scan(c.stats); 2135 addVars(c.stats, initsSwitch, uninitsSwitch); 2136 if (!hasDefault) { 2137 inits.assign(initsSwitch); 2138 uninits.assign(uninits.andSet(uninitsSwitch)); 2139 } 2140 // Warn about fall-through if lint switch fallthrough enabled. 2141 } 2142 if (!hasDefault) { 2143 inits.andSet(initsSwitch); 2144 } 2145 resolveBreaks(tree, prevPendingExits); 2146 nextadr = nextadrPrev; 2147 } 2148 // where 2149 /** Add any variables defined in stats to inits and uninits. */ 2150 private void addVars(List<JCStatement> stats, final Bits inits, 2151 final Bits uninits) { 2152 for (;stats.nonEmpty(); stats = stats.tail) { 2153 JCTree stat = stats.head; 2154 if (stat.hasTag(VARDEF)) { 2155 int adr = ((JCVariableDecl) stat).sym.adr; 2156 inits.excl(adr); 2157 uninits.incl(adr); 2158 } 2159 } 2160 } 2161 2162 public void visitTry(JCTry tree) { 2163 ListBuffer<JCVariableDecl> resourceVarDecls = new ListBuffer<>(); 2164 final Bits uninitsTryPrev = new Bits(uninitsTry); 2165 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 2166 pendingExits = new ListBuffer<>(); 2167 final Bits initsTry = new Bits(inits); 2168 uninitsTry.assign(uninits); 2169 for (JCTree resource : tree.resources) { 2170 if (resource instanceof JCVariableDecl) { 2171 JCVariableDecl vdecl = (JCVariableDecl) resource; 2172 visitVarDef(vdecl); 2173 unrefdResources.enter(vdecl.sym); 2174 resourceVarDecls.append(vdecl); 2175 } else if (resource instanceof JCExpression) { 2176 scanExpr((JCExpression) resource); 2177 } else { 2178 throw new AssertionError(tree); // parser error 2179 } 2180 } 2181 scan(tree.body); 2182 uninitsTry.andSet(uninits); 2183 final Bits initsEnd = new Bits(inits); 2184 final Bits uninitsEnd = new Bits(uninits); 2185 int nextadrCatch = nextadr; 2186 2187 if (!resourceVarDecls.isEmpty() && 2188 lint.isEnabled(Lint.LintCategory.TRY)) { 2189 for (JCVariableDecl resVar : resourceVarDecls) { 2190 if (unrefdResources.includes(resVar.sym)) { 2191 log.warning(Lint.LintCategory.TRY, resVar.pos(), 2192 Warnings.TryResourceNotReferenced(resVar.sym)); 2193 unrefdResources.remove(resVar.sym); 2194 } 2195 } 2196 } 2197 2198 /* The analysis of each catch should be independent. 2199 * Each one should have the same initial values of inits and 2200 * uninits. 2201 */ 2202 final Bits initsCatchPrev = new Bits(initsTry); 2203 final Bits uninitsCatchPrev = new Bits(uninitsTry); 2204 2205 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 2206 JCVariableDecl param = l.head.param; 2207 inits.assign(initsCatchPrev); 2208 uninits.assign(uninitsCatchPrev); 2209 scan(param); 2210 /* If this is a TWR and we are executing the code from Gen, 2211 * then there can be synthetic variables, ignore them. 2212 */ 2213 initParam(param); 2214 scan(l.head.body); 2215 initsEnd.andSet(inits); 2216 uninitsEnd.andSet(uninits); 2217 nextadr = nextadrCatch; 2218 } 2219 if (tree.finalizer != null) { 2220 inits.assign(initsTry); 2221 uninits.assign(uninitsTry); 2222 ListBuffer<AssignPendingExit> exits = pendingExits; 2223 pendingExits = prevPendingExits; 2224 scan(tree.finalizer); 2225 if (!tree.finallyCanCompleteNormally) { 2226 // discard exits and exceptions from try and finally 2227 } else { 2228 uninits.andSet(uninitsEnd); 2229 // FIX: this doesn't preserve source order of exits in catch 2230 // versus finally! 2231 while (exits.nonEmpty()) { 2232 AssignPendingExit exit = exits.next(); 2233 if (exit.exit_inits != null) { 2234 exit.exit_inits.orSet(inits); 2235 exit.exit_uninits.andSet(uninits); 2236 } 2237 pendingExits.append(exit); 2238 } 2239 inits.orSet(initsEnd); 2240 } 2241 } else { 2242 inits.assign(initsEnd); 2243 uninits.assign(uninitsEnd); 2244 ListBuffer<AssignPendingExit> exits = pendingExits; 2245 pendingExits = prevPendingExits; 2246 while (exits.nonEmpty()) pendingExits.append(exits.next()); 2247 } 2248 uninitsTry.andSet(uninitsTryPrev).andSet(uninits); 2249 } 2250 2251 public void visitConditional(JCConditional tree) { 2252 scanCond(tree.cond); 2253 final Bits initsBeforeElse = new Bits(initsWhenFalse); 2254 final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse); 2255 inits.assign(initsWhenTrue); 2256 uninits.assign(uninitsWhenTrue); 2257 if (tree.truepart.type.hasTag(BOOLEAN) && 2258 tree.falsepart.type.hasTag(BOOLEAN)) { 2259 // if b and c are boolean valued, then 2260 // v is (un)assigned after a?b:c when true iff 2261 // v is (un)assigned after b when true and 2262 // v is (un)assigned after c when true 2263 scanCond(tree.truepart); 2264 final Bits initsAfterThenWhenTrue = new Bits(initsWhenTrue); 2265 final Bits initsAfterThenWhenFalse = new Bits(initsWhenFalse); 2266 final Bits uninitsAfterThenWhenTrue = new Bits(uninitsWhenTrue); 2267 final Bits uninitsAfterThenWhenFalse = new Bits(uninitsWhenFalse); 2268 inits.assign(initsBeforeElse); 2269 uninits.assign(uninitsBeforeElse); 2270 scanCond(tree.falsepart); 2271 initsWhenTrue.andSet(initsAfterThenWhenTrue); 2272 initsWhenFalse.andSet(initsAfterThenWhenFalse); 2273 uninitsWhenTrue.andSet(uninitsAfterThenWhenTrue); 2274 uninitsWhenFalse.andSet(uninitsAfterThenWhenFalse); 2275 } else { 2276 scanExpr(tree.truepart); 2277 final Bits initsAfterThen = new Bits(inits); 2278 final Bits uninitsAfterThen = new Bits(uninits); 2279 inits.assign(initsBeforeElse); 2280 uninits.assign(uninitsBeforeElse); 2281 scanExpr(tree.falsepart); 2282 inits.andSet(initsAfterThen); 2283 uninits.andSet(uninitsAfterThen); 2284 } 2285 } 2286 2287 public void visitIf(JCIf tree) { 2288 scanCond(tree.cond); 2289 final Bits initsBeforeElse = new Bits(initsWhenFalse); 2290 final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse); 2291 inits.assign(initsWhenTrue); 2292 uninits.assign(uninitsWhenTrue); 2293 scan(tree.thenpart); 2294 if (tree.elsepart != null) { 2295 final Bits initsAfterThen = new Bits(inits); 2296 final Bits uninitsAfterThen = new Bits(uninits); 2297 inits.assign(initsBeforeElse); 2298 uninits.assign(uninitsBeforeElse); 2299 scan(tree.elsepart); 2300 inits.andSet(initsAfterThen); 2301 uninits.andSet(uninitsAfterThen); 2302 } else { 2303 inits.andSet(initsBeforeElse); 2304 uninits.andSet(uninitsBeforeElse); 2305 } 2306 } 2307 2308 @Override 2309 public void visitBreak(JCBreak tree) { 2310 recordExit(new AssignPendingExit(tree, inits, uninits)); 2311 } 2312 2313 @Override 2314 public void visitContinue(JCContinue tree) { 2315 recordExit(new AssignPendingExit(tree, inits, uninits)); 2316 } 2317 2318 @Override 2319 public void visitReturn(JCReturn tree) { 2320 scanExpr(tree.expr); 2321 recordExit(new AssignPendingExit(tree, inits, uninits)); 2322 } 2323 2324 public void visitThrow(JCThrow tree) { 2325 scanExpr(tree.expr); 2326 markDead(); 2327 } 2328 2329 public void visitApply(JCMethodInvocation tree) { 2330 scanExpr(tree.meth); 2331 scanExprs(tree.args); 2332 } 2333 2334 public void visitNewClass(JCNewClass tree) { 2335 scanExpr(tree.encl); 2336 scanExprs(tree.args); 2337 scan(tree.def); 2338 } 2339 2340 @Override 2341 public void visitLambda(JCLambda tree) { 2342 final Bits prevUninits = new Bits(uninits); 2343 final Bits prevInits = new Bits(inits); 2344 int returnadrPrev = returnadr; 2345 int nextadrPrev = nextadr; 2346 ListBuffer<AssignPendingExit> prevPending = pendingExits; 2347 try { 2348 returnadr = nextadr; 2349 pendingExits = new ListBuffer<>(); 2350 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { 2351 JCVariableDecl def = l.head; 2352 scan(def); 2353 inits.incl(def.sym.adr); 2354 uninits.excl(def.sym.adr); 2355 } 2356 if (tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION) { 2357 scanExpr(tree.body); 2358 } else { 2359 scan(tree.body); 2360 } 2361 } 2362 finally { 2363 returnadr = returnadrPrev; 2364 uninits.assign(prevUninits); 2365 inits.assign(prevInits); 2366 pendingExits = prevPending; 2367 nextadr = nextadrPrev; 2368 } 2369 } 2370 2371 public void visitNewArray(JCNewArray tree) { 2372 scanExprs(tree.dims); 2373 scanExprs(tree.elems); 2374 } 2375 2376 public void visitAssert(JCAssert tree) { 2377 final Bits initsExit = new Bits(inits); 2378 final Bits uninitsExit = new Bits(uninits); 2379 scanCond(tree.cond); 2380 uninitsExit.andSet(uninitsWhenTrue); 2381 if (tree.detail != null) { 2382 inits.assign(initsWhenFalse); 2383 uninits.assign(uninitsWhenFalse); 2384 scanExpr(tree.detail); 2385 } 2386 inits.assign(initsExit); 2387 uninits.assign(uninitsExit); 2388 } 2389 2390 public void visitAssign(JCAssign tree) { 2391 JCTree lhs = TreeInfo.skipParens(tree.lhs); 2392 if (!isIdentOrThisDotIdent(lhs)) 2393 scanExpr(lhs); 2394 scanExpr(tree.rhs); 2395 letInit(lhs); 2396 } 2397 private boolean isIdentOrThisDotIdent(JCTree lhs) { 2398 if (lhs.hasTag(IDENT)) 2399 return true; 2400 if (!lhs.hasTag(SELECT)) 2401 return false; 2402 2403 JCFieldAccess fa = (JCFieldAccess)lhs; 2404 return fa.selected.hasTag(IDENT) && 2405 ((JCIdent)fa.selected).name == names._this; 2406 } 2407 2408 // check fields accessed through this.<field> are definitely 2409 // assigned before reading their value 2410 public void visitSelect(JCFieldAccess tree) { 2411 super.visitSelect(tree); 2412 JCTree sel = TreeInfo.skipParens(tree.selected); 2413 if (enforceThisDotInit && 2414 sel.hasTag(IDENT) && 2415 ((JCIdent)sel).name == names._this && 2416 tree.sym.kind == VAR) { 2417 checkInit(tree.pos(), (VarSymbol)tree.sym); 2418 } 2419 } 2420 2421 public void visitAssignop(JCAssignOp tree) { 2422 scanExpr(tree.lhs); 2423 scanExpr(tree.rhs); 2424 letInit(tree.lhs); 2425 } 2426 2427 public void visitUnary(JCUnary tree) { 2428 switch (tree.getTag()) { 2429 case NOT: 2430 scanCond(tree.arg); 2431 final Bits t = new Bits(initsWhenFalse); 2432 initsWhenFalse.assign(initsWhenTrue); 2433 initsWhenTrue.assign(t); 2434 t.assign(uninitsWhenFalse); 2435 uninitsWhenFalse.assign(uninitsWhenTrue); 2436 uninitsWhenTrue.assign(t); 2437 break; 2438 case PREINC: case POSTINC: 2439 case PREDEC: case POSTDEC: 2440 scanExpr(tree.arg); 2441 letInit(tree.arg); 2442 break; 2443 default: 2444 scanExpr(tree.arg); 2445 } 2446 } 2447 2448 public void visitBinary(JCBinary tree) { 2449 switch (tree.getTag()) { 2450 case AND: 2451 scanCond(tree.lhs); 2452 final Bits initsWhenFalseLeft = new Bits(initsWhenFalse); 2453 final Bits uninitsWhenFalseLeft = new Bits(uninitsWhenFalse); 2454 inits.assign(initsWhenTrue); 2455 uninits.assign(uninitsWhenTrue); 2456 scanCond(tree.rhs); 2457 initsWhenFalse.andSet(initsWhenFalseLeft); 2458 uninitsWhenFalse.andSet(uninitsWhenFalseLeft); 2459 break; 2460 case OR: 2461 scanCond(tree.lhs); 2462 final Bits initsWhenTrueLeft = new Bits(initsWhenTrue); 2463 final Bits uninitsWhenTrueLeft = new Bits(uninitsWhenTrue); 2464 inits.assign(initsWhenFalse); 2465 uninits.assign(uninitsWhenFalse); 2466 scanCond(tree.rhs); 2467 initsWhenTrue.andSet(initsWhenTrueLeft); 2468 uninitsWhenTrue.andSet(uninitsWhenTrueLeft); 2469 break; 2470 default: 2471 scanExpr(tree.lhs); 2472 scanExpr(tree.rhs); 2473 } 2474 } 2475 2476 public void visitIdent(JCIdent tree) { 2477 if (tree.sym.kind == VAR) { 2478 checkInit(tree.pos(), (VarSymbol)tree.sym); 2479 referenced(tree.sym); 2480 } 2481 } 2482 2483 void referenced(Symbol sym) { 2484 unrefdResources.remove(sym); 2485 } 2486 2487 public void visitAnnotatedType(JCAnnotatedType tree) { 2488 // annotations don't get scanned 2489 tree.underlyingType.accept(this); 2490 } 2491 2492 public void visitModuleDef(JCModuleDecl tree) { 2493 // Do nothing for modules 2494 } 2495 2496 /************************************************************************** 2497 * main method 2498 *************************************************************************/ 2499 2500 /** Perform definite assignment/unassignment analysis on a tree. 2501 */ 2502 public void analyzeTree(Env<?> env) { 2503 analyzeTree(env, env.tree); 2504 } 2505 2506 public void analyzeTree(Env<?> env, JCTree tree) { 2507 try { 2508 startPos = tree.pos().getStartPosition(); 2509 2510 if (vardecls == null) 2511 vardecls = new JCVariableDecl[32]; 2512 else 2513 for (int i=0; i<vardecls.length; i++) 2514 vardecls[i] = null; 2515 firstadr = 0; 2516 nextadr = 0; 2517 pendingExits = new ListBuffer<>(); 2518 this.classDef = null; 2519 unrefdResources = WriteableScope.create(env.enclClass.sym); 2520 scan(tree); 2521 } finally { 2522 // note that recursive invocations of this method fail hard 2523 startPos = -1; 2524 resetBits(inits, uninits, uninitsTry, initsWhenTrue, 2525 initsWhenFalse, uninitsWhenTrue, uninitsWhenFalse); 2526 if (vardecls != null) { 2527 for (int i=0; i<vardecls.length; i++) 2528 vardecls[i] = null; 2529 } 2530 firstadr = 0; 2531 nextadr = 0; 2532 pendingExits = null; 2533 this.classDef = null; 2534 unrefdResources = null; 2535 } 2536 } 2537 } 2538 2539 /** 2540 * This pass implements the last step of the dataflow analysis, namely 2541 * the effectively-final analysis check. This checks that every local variable 2542 * reference from a lambda body/local inner class is either final or effectively final. 2543 * Additional this also checks that every variable that is used as an operand to 2544 * try-with-resources is final or effectively final. 2545 * As effectively final variables are marked as such during DA/DU, this pass must run after 2546 * AssignAnalyzer. 2547 */ 2548 class CaptureAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> { 2549 2550 JCTree currentTree; //local class or lambda 2551 2552 @Override 2553 void markDead() { 2554 //do nothing 2555 } 2556 2557 @SuppressWarnings("fallthrough") 2558 void checkEffectivelyFinal(DiagnosticPosition pos, VarSymbol sym) { 2559 if (currentTree != null && 2560 sym.owner.kind == MTH && 2561 sym.pos < currentTree.getStartPosition()) { 2562 switch (currentTree.getTag()) { 2563 case CLASSDEF: 2564 if (!allowEffectivelyFinalInInnerClasses) { 2565 if ((sym.flags() & FINAL) == 0) { 2566 reportInnerClsNeedsFinalError(pos, sym); 2567 } 2568 break; 2569 } 2570 case LAMBDA: 2571 if ((sym.flags() & (EFFECTIVELY_FINAL | FINAL)) == 0) { 2572 reportEffectivelyFinalError(pos, sym); 2573 } 2574 } 2575 } 2576 } 2577 2578 @SuppressWarnings("fallthrough") 2579 void letInit(JCTree tree) { 2580 tree = TreeInfo.skipParens(tree); 2581 if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { 2582 Symbol sym = TreeInfo.symbol(tree); 2583 if (currentTree != null && 2584 sym.kind == VAR && 2585 sym.owner.kind == MTH && 2586 ((VarSymbol)sym).pos < currentTree.getStartPosition()) { 2587 switch (currentTree.getTag()) { 2588 case CLASSDEF: 2589 if (!allowEffectivelyFinalInInnerClasses) { 2590 reportInnerClsNeedsFinalError(tree, sym); 2591 break; 2592 } 2593 case LAMBDA: 2594 reportEffectivelyFinalError(tree, sym); 2595 } 2596 } 2597 } 2598 } 2599 2600 void reportEffectivelyFinalError(DiagnosticPosition pos, Symbol sym) { 2601 String subKey = currentTree.hasTag(LAMBDA) ? 2602 "lambda" : "inner.cls"; 2603 log.error(pos, Errors.CantRefNonEffectivelyFinalVar(sym, diags.fragment(subKey))); 2604 } 2605 2606 void reportInnerClsNeedsFinalError(DiagnosticPosition pos, Symbol sym) { 2607 log.error(pos, 2608 Errors.LocalVarAccessedFromIclsNeedsFinal(sym)); 2609 } 2610 2611 /************************************************************************* 2612 * Visitor methods for statements and definitions 2613 *************************************************************************/ 2614 2615 /* ------------ Visitor methods for various sorts of trees -------------*/ 2616 2617 public void visitClassDef(JCClassDecl tree) { 2618 JCTree prevTree = currentTree; 2619 try { 2620 currentTree = tree.sym.isLocal() ? tree : null; 2621 super.visitClassDef(tree); 2622 } finally { 2623 currentTree = prevTree; 2624 } 2625 } 2626 2627 @Override 2628 public void visitLambda(JCLambda tree) { 2629 JCTree prevTree = currentTree; 2630 try { 2631 currentTree = tree; 2632 super.visitLambda(tree); 2633 } finally { 2634 currentTree = prevTree; 2635 } 2636 } 2637 2638 @Override 2639 public void visitIdent(JCIdent tree) { 2640 if (tree.sym.kind == VAR) { 2641 checkEffectivelyFinal(tree, (VarSymbol)tree.sym); 2642 } 2643 } 2644 2645 public void visitAssign(JCAssign tree) { 2646 JCTree lhs = TreeInfo.skipParens(tree.lhs); 2647 if (!(lhs instanceof JCIdent)) { 2648 scan(lhs); 2649 } 2650 scan(tree.rhs); 2651 letInit(lhs); 2652 } 2653 2654 public void visitAssignop(JCAssignOp tree) { 2655 scan(tree.lhs); 2656 scan(tree.rhs); 2657 letInit(tree.lhs); 2658 } 2659 2660 public void visitUnary(JCUnary tree) { 2661 switch (tree.getTag()) { 2662 case PREINC: case POSTINC: 2663 case PREDEC: case POSTDEC: 2664 scan(tree.arg); 2665 letInit(tree.arg); 2666 break; 2667 default: 2668 scan(tree.arg); 2669 } 2670 } 2671 2672 public void visitTry(JCTry tree) { 2673 for (JCTree resource : tree.resources) { 2674 if (!resource.hasTag(VARDEF)) { 2675 Symbol var = TreeInfo.symbol(resource); 2676 if (var != null && (var.flags() & (FINAL | EFFECTIVELY_FINAL)) == 0) { 2677 log.error(resource.pos(), Errors.TryWithResourcesExprEffectivelyFinalVar(var)); 2678 } 2679 } 2680 } 2681 super.visitTry(tree); 2682 } 2683 2684 public void visitModuleDef(JCModuleDecl tree) { 2685 // Do nothing for modules 2686 } 2687 2688 /************************************************************************** 2689 * main method 2690 *************************************************************************/ 2691 2692 /** Perform definite assignment/unassignment analysis on a tree. 2693 */ 2694 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 2695 analyzeTree(env, env.tree, make); 2696 } 2697 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) { 2698 try { 2699 attrEnv = env; 2700 Flow.this.make = make; 2701 pendingExits = new ListBuffer<>(); 2702 scan(tree); 2703 } finally { 2704 pendingExits = null; 2705 Flow.this.make = null; 2706 } 2707 } 2708 } 2709} 2710