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