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