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