214 Source source = Source.instance(context); |
217 Source source = Source.instance(context); |
215 allowImprovedRethrowAnalysis = source.allowImprovedRethrowAnalysis(); |
218 allowImprovedRethrowAnalysis = source.allowImprovedRethrowAnalysis(); |
216 allowImprovedCatchAnalysis = source.allowImprovedCatchAnalysis(); |
219 allowImprovedCatchAnalysis = source.allowImprovedCatchAnalysis(); |
217 } |
220 } |
218 |
221 |
219 /** A flag that indicates whether the last statement could |
222 /** |
220 * complete normally. |
223 * Base visitor class for all visitors implementing dataflow analysis logic. |
|
224 * This class define the shared logic for handling jumps (break/continue statements). |
221 */ |
225 */ |
222 private boolean alive; |
226 static abstract class BaseAnalyzer<P extends BaseAnalyzer.PendingExit> extends TreeScanner { |
223 |
227 |
224 /** The set of definitely assigned variables. |
228 enum JumpKind { |
|
229 BREAK(JCTree.Tag.BREAK) { |
|
230 @Override |
|
231 JCTree getTarget(JCTree tree) { |
|
232 return ((JCBreak)tree).target; |
|
233 } |
|
234 }, |
|
235 CONTINUE(JCTree.Tag.CONTINUE) { |
|
236 @Override |
|
237 JCTree getTarget(JCTree tree) { |
|
238 return ((JCContinue)tree).target; |
|
239 } |
|
240 }; |
|
241 |
|
242 JCTree.Tag treeTag; |
|
243 |
|
244 private JumpKind(Tag treeTag) { |
|
245 this.treeTag = treeTag; |
|
246 } |
|
247 |
|
248 abstract JCTree getTarget(JCTree tree); |
|
249 } |
|
250 |
|
251 /** The currently pending exits that go from current inner blocks |
|
252 * to an enclosing block, in source order. |
|
253 */ |
|
254 ListBuffer<P> pendingExits; |
|
255 |
|
256 /** A pending exit. These are the statements return, break, and |
|
257 * continue. In addition, exception-throwing expressions or |
|
258 * statements are put here when not known to be caught. This |
|
259 * will typically result in an error unless it is within a |
|
260 * try-finally whose finally block cannot complete normally. |
|
261 */ |
|
262 abstract static class PendingExit { |
|
263 JCTree tree; |
|
264 |
|
265 PendingExit(JCTree tree) { |
|
266 this.tree = tree; |
|
267 } |
|
268 |
|
269 abstract void resolveJump(); |
|
270 } |
|
271 |
|
272 abstract void markDead(); |
|
273 |
|
274 /** Record an outward transfer of control. */ |
|
275 void recordExit(JCTree tree, P pe) { |
|
276 pendingExits.append(pe); |
|
277 markDead(); |
|
278 } |
|
279 |
|
280 /** Resolve all jumps of this statement. */ |
|
281 private boolean resolveJump(JCTree tree, |
|
282 ListBuffer<P> oldPendingExits, |
|
283 JumpKind jk) { |
|
284 boolean resolved = false; |
|
285 List<P> exits = pendingExits.toList(); |
|
286 pendingExits = oldPendingExits; |
|
287 for (; exits.nonEmpty(); exits = exits.tail) { |
|
288 P exit = exits.head; |
|
289 if (exit.tree.hasTag(jk.treeTag) && |
|
290 jk.getTarget(exit.tree) == tree) { |
|
291 exit.resolveJump(); |
|
292 resolved = true; |
|
293 } else { |
|
294 pendingExits.append(exit); |
|
295 } |
|
296 } |
|
297 return resolved; |
|
298 } |
|
299 |
|
300 /** Resolve all breaks of this statement. */ |
|
301 boolean resolveContinues(JCTree tree) { |
|
302 return resolveJump(tree, new ListBuffer<P>(), JumpKind.CONTINUE); |
|
303 } |
|
304 |
|
305 /** Resolve all continues of this statement. */ |
|
306 boolean resolveBreaks(JCTree tree, ListBuffer<P> oldPendingExits) { |
|
307 return resolveJump(tree, oldPendingExits, JumpKind.BREAK); |
|
308 } |
|
309 } |
|
310 |
|
311 /** |
|
312 * This pass implements the first two steps of the dataflow analysis: |
|
313 * (i) liveness analysis checks that every statement is reachable and (ii) |
|
314 * exception analysis to ensure that every checked exception that is |
|
315 * thrown is declared or caught. |
225 */ |
316 */ |
226 Bits inits; |
317 class FlowAnalyzer extends BaseAnalyzer<FlowAnalyzer.FlowPendingExit> { |
227 |
318 |
228 /** The set of definitely unassigned variables. |
319 /** A flag that indicates whether the last statement could |
229 */ |
320 * complete normally. |
230 Bits uninits; |
321 */ |
231 |
322 private boolean alive; |
232 HashMap<Symbol, List<Type>> preciseRethrowTypes; |
323 |
233 |
324 HashMap<Symbol, List<Type>> preciseRethrowTypes; |
234 /** The set of variables that are definitely unassigned everywhere |
325 |
235 * in current try block. This variable is maintained lazily; it is |
326 /** The current class being defined. |
236 * updated only when something gets removed from uninits, |
327 */ |
237 * typically by being assigned in reachable code. To obtain the |
328 JCClassDecl classDef; |
238 * correct set of variables which are definitely unassigned |
329 |
239 * anywhere in current try block, intersect uninitsTry and |
330 /** The list of possibly thrown declarable exceptions. |
240 * uninits. |
331 */ |
241 */ |
332 List<Type> thrown; |
242 Bits uninitsTry; |
333 |
243 |
334 /** The list of exceptions that are either caught or declared to be |
244 /** When analyzing a condition, inits and uninits are null. |
335 * thrown. |
245 * Instead we have: |
336 */ |
246 */ |
337 List<Type> caught; |
247 Bits initsWhenTrue; |
338 |
248 Bits initsWhenFalse; |
339 class FlowPendingExit extends BaseAnalyzer.PendingExit { |
249 Bits uninitsWhenTrue; |
340 |
250 Bits uninitsWhenFalse; |
341 Type thrown; |
251 |
342 |
252 /** A mapping from addresses to variable symbols. |
343 FlowPendingExit(JCTree tree, Type thrown) { |
253 */ |
344 super(tree); |
254 VarSymbol[] vars; |
345 this.thrown = thrown; |
255 |
346 } |
256 /** The current class being defined. |
347 |
257 */ |
348 void resolveJump() { /*do nothing*/ } |
258 JCClassDecl classDef; |
349 } |
259 |
350 |
260 /** The first variable sequence number in this class definition. |
351 @Override |
261 */ |
352 void markDead() { |
262 int firstadr; |
353 alive = false; |
263 |
354 } |
264 /** The next available variable sequence number. |
355 |
265 */ |
356 /*-------------------- Exceptions ----------------------*/ |
266 int nextadr; |
357 |
267 |
358 /** Complain that pending exceptions are not caught. |
268 /** The list of possibly thrown declarable exceptions. |
359 */ |
269 */ |
360 void errorUncaught() { |
270 List<Type> thrown; |
361 for (FlowPendingExit exit = pendingExits.next(); |
271 |
362 exit != null; |
272 /** The list of exceptions that are either caught or declared to be |
363 exit = pendingExits.next()) { |
273 * thrown. |
364 if (classDef != null && |
274 */ |
365 classDef.pos == exit.tree.pos) { |
275 List<Type> caught; |
366 log.error(exit.tree.pos(), |
276 |
367 "unreported.exception.default.constructor", |
277 /** The list of unreferenced automatic resources. |
368 exit.thrown); |
278 */ |
369 } else if (exit.tree.hasTag(VARDEF) && |
279 Scope unrefdResources; |
370 ((JCVariableDecl)exit.tree).sym.isResourceVariable()) { |
280 |
371 log.error(exit.tree.pos(), |
281 /** Set when processing a loop body the second time for DU analysis. */ |
372 "unreported.exception.implicit.close", |
282 boolean loopPassTwo = false; |
373 exit.thrown, |
283 |
374 ((JCVariableDecl)exit.tree).sym.name); |
284 /*-------------------- Environments ----------------------*/ |
|
285 |
|
286 /** A pending exit. These are the statements return, break, and |
|
287 * continue. In addition, exception-throwing expressions or |
|
288 * statements are put here when not known to be caught. This |
|
289 * will typically result in an error unless it is within a |
|
290 * try-finally whose finally block cannot complete normally. |
|
291 */ |
|
292 static class PendingExit { |
|
293 JCTree tree; |
|
294 Bits inits; |
|
295 Bits uninits; |
|
296 Type thrown; |
|
297 PendingExit(JCTree tree, Bits inits, Bits uninits) { |
|
298 this.tree = tree; |
|
299 this.inits = inits.dup(); |
|
300 this.uninits = uninits.dup(); |
|
301 } |
|
302 PendingExit(JCTree tree, Type thrown) { |
|
303 this.tree = tree; |
|
304 this.thrown = thrown; |
|
305 } |
|
306 } |
|
307 |
|
308 /** The currently pending exits that go from current inner blocks |
|
309 * to an enclosing block, in source order. |
|
310 */ |
|
311 ListBuffer<PendingExit> pendingExits; |
|
312 |
|
313 /*-------------------- Exceptions ----------------------*/ |
|
314 |
|
315 /** Complain that pending exceptions are not caught. |
|
316 */ |
|
317 void errorUncaught() { |
|
318 for (PendingExit exit = pendingExits.next(); |
|
319 exit != null; |
|
320 exit = pendingExits.next()) { |
|
321 if (classDef != null && |
|
322 classDef.pos == exit.tree.pos) { |
|
323 log.error(exit.tree.pos(), |
|
324 "unreported.exception.default.constructor", |
|
325 exit.thrown); |
|
326 } else if (exit.tree.hasTag(VARDEF) && |
|
327 ((JCVariableDecl)exit.tree).sym.isResourceVariable()) { |
|
328 log.error(exit.tree.pos(), |
|
329 "unreported.exception.implicit.close", |
|
330 exit.thrown, |
|
331 ((JCVariableDecl)exit.tree).sym.name); |
|
332 } else { |
|
333 log.error(exit.tree.pos(), |
|
334 "unreported.exception.need.to.catch.or.throw", |
|
335 exit.thrown); |
|
336 } |
|
337 } |
|
338 } |
|
339 |
|
340 /** Record that exception is potentially thrown and check that it |
|
341 * is caught. |
|
342 */ |
|
343 void markThrown(JCTree tree, Type exc) { |
|
344 if (!chk.isUnchecked(tree.pos(), exc)) { |
|
345 if (!chk.isHandled(exc, caught)) |
|
346 pendingExits.append(new PendingExit(tree, exc)); |
|
347 thrown = chk.incl(exc, thrown); |
|
348 } |
|
349 } |
|
350 |
|
351 /*-------------- Processing variables ----------------------*/ |
|
352 |
|
353 /** Do we need to track init/uninit state of this symbol? |
|
354 * I.e. is symbol either a local or a blank final variable? |
|
355 */ |
|
356 boolean trackable(VarSymbol sym) { |
|
357 return |
|
358 (sym.owner.kind == MTH || |
|
359 ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL && |
|
360 classDef.sym.isEnclosedBy((ClassSymbol)sym.owner))); |
|
361 } |
|
362 |
|
363 /** Initialize new trackable variable by setting its address field |
|
364 * to the next available sequence number and entering it under that |
|
365 * index into the vars array. |
|
366 */ |
|
367 void newVar(VarSymbol sym) { |
|
368 if (nextadr == vars.length) { |
|
369 VarSymbol[] newvars = new VarSymbol[nextadr * 2]; |
|
370 System.arraycopy(vars, 0, newvars, 0, nextadr); |
|
371 vars = newvars; |
|
372 } |
|
373 sym.adr = nextadr; |
|
374 vars[nextadr] = sym; |
|
375 inits.excl(nextadr); |
|
376 uninits.incl(nextadr); |
|
377 nextadr++; |
|
378 } |
|
379 |
|
380 /** Record an initialization of a trackable variable. |
|
381 */ |
|
382 void letInit(DiagnosticPosition pos, VarSymbol sym) { |
|
383 if (sym.adr >= firstadr && trackable(sym)) { |
|
384 if ((sym.flags() & FINAL) != 0) { |
|
385 if ((sym.flags() & PARAMETER) != 0) { |
|
386 if ((sym.flags() & UNION) != 0) { //multi-catch parameter |
|
387 log.error(pos, "multicatch.parameter.may.not.be.assigned", |
|
388 sym); |
|
389 } |
|
390 else { |
|
391 log.error(pos, "final.parameter.may.not.be.assigned", |
|
392 sym); |
|
393 } |
|
394 } else if (!uninits.isMember(sym.adr)) { |
|
395 log.error(pos, |
|
396 loopPassTwo |
|
397 ? "var.might.be.assigned.in.loop" |
|
398 : "var.might.already.be.assigned", |
|
399 sym); |
|
400 } else if (!inits.isMember(sym.adr)) { |
|
401 // reachable assignment |
|
402 uninits.excl(sym.adr); |
|
403 uninitsTry.excl(sym.adr); |
|
404 } else { |
375 } else { |
405 //log.rawWarning(pos, "unreachable assignment");//DEBUG |
376 log.error(exit.tree.pos(), |
406 uninits.excl(sym.adr); |
377 "unreported.exception.need.to.catch.or.throw", |
407 } |
378 exit.thrown); |
408 } |
379 } |
409 inits.incl(sym.adr); |
380 } |
410 } else if ((sym.flags() & FINAL) != 0) { |
381 } |
411 log.error(pos, "var.might.already.be.assigned", sym); |
382 |
412 } |
383 /** Record that exception is potentially thrown and check that it |
413 } |
384 * is caught. |
414 |
385 */ |
415 /** If tree is either a simple name or of the form this.name or |
386 void markThrown(JCTree tree, Type exc) { |
416 * C.this.name, and tree represents a trackable variable, |
387 if (!chk.isUnchecked(tree.pos(), exc)) { |
417 * record an initialization of the variable. |
388 if (!chk.isHandled(exc, caught)) |
418 */ |
389 pendingExits.append(new FlowPendingExit(tree, exc)); |
419 void letInit(JCTree tree) { |
390 thrown = chk.incl(exc, thrown); |
420 tree = TreeInfo.skipParens(tree); |
391 } |
421 if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { |
392 } |
422 Symbol sym = TreeInfo.symbol(tree); |
393 |
423 if (sym.kind == VAR) { |
394 /************************************************************************* |
424 letInit(tree.pos(), (VarSymbol)sym); |
395 * Visitor methods for statements and definitions |
425 } |
396 *************************************************************************/ |
426 } |
397 |
427 } |
398 /** Analyze a definition. |
428 |
399 */ |
429 /** Check that trackable variable is initialized. |
400 void scanDef(JCTree tree) { |
430 */ |
401 scanStat(tree); |
431 void checkInit(DiagnosticPosition pos, VarSymbol sym) { |
402 if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && !alive) { |
432 if ((sym.adr >= firstadr || sym.owner.kind != TYP) && |
403 log.error(tree.pos(), |
433 trackable(sym) && |
404 "initializer.must.be.able.to.complete.normally"); |
434 !inits.isMember(sym.adr)) { |
405 } |
435 log.error(pos, "var.might.not.have.been.initialized", |
406 } |
436 sym); |
407 |
437 inits.incl(sym.adr); |
408 /** Analyze a statement. Check that statement is reachable. |
438 } |
409 */ |
439 } |
410 void scanStat(JCTree tree) { |
440 |
411 if (!alive && tree != null) { |
441 /*-------------------- Handling jumps ----------------------*/ |
412 log.error(tree.pos(), "unreachable.stmt"); |
442 |
413 if (!tree.hasTag(SKIP)) alive = true; |
443 /** Record an outward transfer of control. */ |
414 } |
444 void recordExit(JCTree tree) { |
|
445 pendingExits.append(new PendingExit(tree, inits, uninits)); |
|
446 markDead(); |
|
447 } |
|
448 |
|
449 /** Resolve all breaks of this statement. */ |
|
450 boolean resolveBreaks(JCTree tree, |
|
451 ListBuffer<PendingExit> oldPendingExits) { |
|
452 boolean result = false; |
|
453 List<PendingExit> exits = pendingExits.toList(); |
|
454 pendingExits = oldPendingExits; |
|
455 for (; exits.nonEmpty(); exits = exits.tail) { |
|
456 PendingExit exit = exits.head; |
|
457 if (exit.tree.hasTag(BREAK) && |
|
458 ((JCBreak) exit.tree).target == tree) { |
|
459 inits.andSet(exit.inits); |
|
460 uninits.andSet(exit.uninits); |
|
461 result = true; |
|
462 } else { |
|
463 pendingExits.append(exit); |
|
464 } |
|
465 } |
|
466 return result; |
|
467 } |
|
468 |
|
469 /** Resolve all continues of this statement. */ |
|
470 boolean resolveContinues(JCTree tree) { |
|
471 boolean result = false; |
|
472 List<PendingExit> exits = pendingExits.toList(); |
|
473 pendingExits = new ListBuffer<PendingExit>(); |
|
474 for (; exits.nonEmpty(); exits = exits.tail) { |
|
475 PendingExit exit = exits.head; |
|
476 if (exit.tree.hasTag(CONTINUE) && |
|
477 ((JCContinue) exit.tree).target == tree) { |
|
478 inits.andSet(exit.inits); |
|
479 uninits.andSet(exit.uninits); |
|
480 result = true; |
|
481 } else { |
|
482 pendingExits.append(exit); |
|
483 } |
|
484 } |
|
485 return result; |
|
486 } |
|
487 |
|
488 /** Record that statement is unreachable. |
|
489 */ |
|
490 void markDead() { |
|
491 inits.inclRange(firstadr, nextadr); |
|
492 uninits.inclRange(firstadr, nextadr); |
|
493 alive = false; |
|
494 } |
|
495 |
|
496 /** Split (duplicate) inits/uninits into WhenTrue/WhenFalse sets |
|
497 */ |
|
498 void split(boolean setToNull) { |
|
499 initsWhenFalse = inits.dup(); |
|
500 uninitsWhenFalse = uninits.dup(); |
|
501 initsWhenTrue = inits; |
|
502 uninitsWhenTrue = uninits; |
|
503 if (setToNull) |
|
504 inits = uninits = null; |
|
505 } |
|
506 |
|
507 /** Merge (intersect) inits/uninits from WhenTrue/WhenFalse sets. |
|
508 */ |
|
509 void merge() { |
|
510 inits = initsWhenFalse.andSet(initsWhenTrue); |
|
511 uninits = uninitsWhenFalse.andSet(uninitsWhenTrue); |
|
512 } |
|
513 |
|
514 /* ************************************************************************ |
|
515 * Visitor methods for statements and definitions |
|
516 *************************************************************************/ |
|
517 |
|
518 /** Analyze a definition. |
|
519 */ |
|
520 void scanDef(JCTree tree) { |
|
521 scanStat(tree); |
|
522 if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && !alive) { |
|
523 log.error(tree.pos(), |
|
524 "initializer.must.be.able.to.complete.normally"); |
|
525 } |
|
526 } |
|
527 |
|
528 /** Analyze a statement. Check that statement is reachable. |
|
529 */ |
|
530 void scanStat(JCTree tree) { |
|
531 if (!alive && tree != null) { |
|
532 log.error(tree.pos(), "unreachable.stmt"); |
|
533 if (!tree.hasTag(SKIP)) alive = true; |
|
534 } |
|
535 scan(tree); |
|
536 } |
|
537 |
|
538 /** Analyze list of statements. |
|
539 */ |
|
540 void scanStats(List<? extends JCStatement> trees) { |
|
541 if (trees != null) |
|
542 for (List<? extends JCStatement> l = trees; l.nonEmpty(); l = l.tail) |
|
543 scanStat(l.head); |
|
544 } |
|
545 |
|
546 /** Analyze an expression. Make sure to set (un)inits rather than |
|
547 * (un)initsWhenTrue(WhenFalse) on exit. |
|
548 */ |
|
549 void scanExpr(JCTree tree) { |
|
550 if (tree != null) { |
|
551 scan(tree); |
415 scan(tree); |
552 if (inits == null) merge(); |
416 } |
553 } |
417 |
554 } |
418 /** Analyze list of statements. |
555 |
419 */ |
556 /** Analyze a list of expressions. |
420 void scanStats(List<? extends JCStatement> trees) { |
557 */ |
421 if (trees != null) |
558 void scanExprs(List<? extends JCExpression> trees) { |
422 for (List<? extends JCStatement> l = trees; l.nonEmpty(); l = l.tail) |
559 if (trees != null) |
423 scanStat(l.head); |
560 for (List<? extends JCExpression> l = trees; l.nonEmpty(); l = l.tail) |
424 } |
561 scanExpr(l.head); |
425 |
562 } |
426 /* ------------ Visitor methods for various sorts of trees -------------*/ |
563 |
427 |
564 /** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse) |
428 public void visitClassDef(JCClassDecl tree) { |
565 * rather than (un)inits on exit. |
429 if (tree.sym == null) return; |
566 */ |
430 |
567 void scanCond(JCTree tree) { |
431 JCClassDecl classDefPrev = classDef; |
568 if (tree.type.isFalse()) { |
432 List<Type> thrownPrev = thrown; |
569 if (inits == null) merge(); |
433 List<Type> caughtPrev = caught; |
570 initsWhenTrue = inits.dup(); |
434 boolean alivePrev = alive; |
571 initsWhenTrue.inclRange(firstadr, nextadr); |
435 ListBuffer<FlowPendingExit> pendingExitsPrev = pendingExits; |
572 uninitsWhenTrue = uninits.dup(); |
436 Lint lintPrev = lint; |
573 uninitsWhenTrue.inclRange(firstadr, nextadr); |
437 |
574 initsWhenFalse = inits; |
438 pendingExits = new ListBuffer<FlowPendingExit>(); |
575 uninitsWhenFalse = uninits; |
|
576 } else if (tree.type.isTrue()) { |
|
577 if (inits == null) merge(); |
|
578 initsWhenFalse = inits.dup(); |
|
579 initsWhenFalse.inclRange(firstadr, nextadr); |
|
580 uninitsWhenFalse = uninits.dup(); |
|
581 uninitsWhenFalse.inclRange(firstadr, nextadr); |
|
582 initsWhenTrue = inits; |
|
583 uninitsWhenTrue = uninits; |
|
584 } else { |
|
585 scan(tree); |
|
586 if (inits != null) |
|
587 split(tree.type != syms.unknownType); |
|
588 } |
|
589 if (tree.type != syms.unknownType) |
|
590 inits = uninits = null; |
|
591 } |
|
592 |
|
593 /* ------------ Visitor methods for various sorts of trees -------------*/ |
|
594 |
|
595 public void visitClassDef(JCClassDecl tree) { |
|
596 if (tree.sym == null) return; |
|
597 |
|
598 JCClassDecl classDefPrev = classDef; |
|
599 List<Type> thrownPrev = thrown; |
|
600 List<Type> caughtPrev = caught; |
|
601 boolean alivePrev = alive; |
|
602 int firstadrPrev = firstadr; |
|
603 int nextadrPrev = nextadr; |
|
604 ListBuffer<PendingExit> pendingExitsPrev = pendingExits; |
|
605 Lint lintPrev = lint; |
|
606 |
|
607 pendingExits = new ListBuffer<PendingExit>(); |
|
608 if (tree.name != names.empty) { |
|
609 caught = List.nil(); |
|
610 firstadr = nextadr; |
|
611 } |
|
612 classDef = tree; |
|
613 thrown = List.nil(); |
|
614 lint = lint.augment(tree.sym.attributes_field); |
|
615 |
|
616 try { |
|
617 // define all the static fields |
|
618 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
|
619 if (l.head.hasTag(VARDEF)) { |
|
620 JCVariableDecl def = (JCVariableDecl)l.head; |
|
621 if ((def.mods.flags & STATIC) != 0) { |
|
622 VarSymbol sym = def.sym; |
|
623 if (trackable(sym)) |
|
624 newVar(sym); |
|
625 } |
|
626 } |
|
627 } |
|
628 |
|
629 // process all the static initializers |
|
630 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
|
631 if (!l.head.hasTag(METHODDEF) && |
|
632 (TreeInfo.flags(l.head) & STATIC) != 0) { |
|
633 scanDef(l.head); |
|
634 errorUncaught(); |
|
635 } |
|
636 } |
|
637 |
|
638 // add intersection of all thrown clauses of initial constructors |
|
639 // to set of caught exceptions, unless class is anonymous. |
|
640 if (tree.name != names.empty) { |
439 if (tree.name != names.empty) { |
641 boolean firstConstructor = true; |
440 caught = List.nil(); |
|
441 } |
|
442 classDef = tree; |
|
443 thrown = List.nil(); |
|
444 lint = lint.augment(tree.sym.attributes_field); |
|
445 |
|
446 try { |
|
447 // process all the static initializers |
642 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
448 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
643 if (TreeInfo.isInitialConstructor(l.head)) { |
449 if (!l.head.hasTag(METHODDEF) && |
644 List<Type> mthrown = |
450 (TreeInfo.flags(l.head) & STATIC) != 0) { |
645 ((JCMethodDecl) l.head).sym.type.getThrownTypes(); |
451 scanDef(l.head); |
646 if (firstConstructor) { |
452 errorUncaught(); |
647 caught = mthrown; |
453 } |
648 firstConstructor = false; |
454 } |
649 } else { |
455 |
650 caught = chk.intersect(mthrown, caught); |
456 // add intersection of all thrown clauses of initial constructors |
|
457 // to set of caught exceptions, unless class is anonymous. |
|
458 if (tree.name != names.empty) { |
|
459 boolean firstConstructor = true; |
|
460 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
|
461 if (TreeInfo.isInitialConstructor(l.head)) { |
|
462 List<Type> mthrown = |
|
463 ((JCMethodDecl) l.head).sym.type.getThrownTypes(); |
|
464 if (firstConstructor) { |
|
465 caught = mthrown; |
|
466 firstConstructor = false; |
|
467 } else { |
|
468 caught = chk.intersect(mthrown, caught); |
|
469 } |
651 } |
470 } |
652 } |
471 } |
653 } |
472 } |
654 } |
473 |
655 |
474 // process all the instance initializers |
656 // define all the instance fields |
|
657 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
|
658 if (l.head.hasTag(VARDEF)) { |
|
659 JCVariableDecl def = (JCVariableDecl)l.head; |
|
660 if ((def.mods.flags & STATIC) == 0) { |
|
661 VarSymbol sym = def.sym; |
|
662 if (trackable(sym)) |
|
663 newVar(sym); |
|
664 } |
|
665 } |
|
666 } |
|
667 |
|
668 // process all the instance initializers |
|
669 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
|
670 if (!l.head.hasTag(METHODDEF) && |
|
671 (TreeInfo.flags(l.head) & STATIC) == 0) { |
|
672 scanDef(l.head); |
|
673 errorUncaught(); |
|
674 } |
|
675 } |
|
676 |
|
677 // in an anonymous class, add the set of thrown exceptions to |
|
678 // the throws clause of the synthetic constructor and propagate |
|
679 // outwards. |
|
680 // Changing the throws clause on the fly is okay here because |
|
681 // the anonymous constructor can't be invoked anywhere else, |
|
682 // and its type hasn't been cached. |
|
683 if (tree.name == names.empty) { |
|
684 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
475 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
685 if (TreeInfo.isInitialConstructor(l.head)) { |
476 if (!l.head.hasTag(METHODDEF) && |
686 JCMethodDecl mdef = (JCMethodDecl)l.head; |
477 (TreeInfo.flags(l.head) & STATIC) == 0) { |
687 mdef.thrown = make.Types(thrown); |
478 scanDef(l.head); |
688 mdef.sym.type = types.createMethodTypeWithThrown(mdef.sym.type, thrown); |
479 errorUncaught(); |
689 } |
480 } |
690 } |
481 } |
691 thrownPrev = chk.union(thrown, thrownPrev); |
482 |
692 } |
483 // in an anonymous class, add the set of thrown exceptions to |
693 |
484 // the throws clause of the synthetic constructor and propagate |
694 // process all the methods |
485 // outwards. |
695 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
486 // Changing the throws clause on the fly is okay here because |
696 if (l.head.hasTag(METHODDEF)) { |
487 // the anonymous constructor can't be invoked anywhere else, |
697 scan(l.head); |
488 // and its type hasn't been cached. |
698 errorUncaught(); |
489 if (tree.name == names.empty) { |
699 } |
490 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
700 } |
491 if (TreeInfo.isInitialConstructor(l.head)) { |
701 |
492 JCMethodDecl mdef = (JCMethodDecl)l.head; |
702 thrown = thrownPrev; |
493 mdef.thrown = make.Types(thrown); |
703 } finally { |
494 mdef.sym.type = types.createMethodTypeWithThrown(mdef.sym.type, thrown); |
704 pendingExits = pendingExitsPrev; |
495 } |
705 alive = alivePrev; |
496 } |
706 nextadr = nextadrPrev; |
497 thrownPrev = chk.union(thrown, thrownPrev); |
707 firstadr = firstadrPrev; |
498 } |
708 caught = caughtPrev; |
499 |
709 classDef = classDefPrev; |
500 // process all the methods |
710 lint = lintPrev; |
501 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
711 } |
502 if (l.head.hasTag(METHODDEF)) { |
712 } |
503 scan(l.head); |
713 |
504 errorUncaught(); |
714 public void visitMethodDef(JCMethodDecl tree) { |
505 } |
715 if (tree.body == null) return; |
506 } |
716 |
507 |
717 List<Type> caughtPrev = caught; |
508 thrown = thrownPrev; |
718 List<Type> mthrown = tree.sym.type.getThrownTypes(); |
509 } finally { |
719 Bits initsPrev = inits.dup(); |
510 pendingExits = pendingExitsPrev; |
720 Bits uninitsPrev = uninits.dup(); |
511 alive = alivePrev; |
721 int nextadrPrev = nextadr; |
512 caught = caughtPrev; |
722 int firstadrPrev = firstadr; |
513 classDef = classDefPrev; |
723 Lint lintPrev = lint; |
514 lint = lintPrev; |
724 |
515 } |
725 lint = lint.augment(tree.sym.attributes_field); |
516 } |
726 |
517 |
727 Assert.check(pendingExits.isEmpty()); |
518 public void visitMethodDef(JCMethodDecl tree) { |
728 |
519 if (tree.body == null) return; |
729 try { |
520 |
730 boolean isInitialConstructor = |
521 List<Type> caughtPrev = caught; |
731 TreeInfo.isInitialConstructor(tree); |
522 List<Type> mthrown = tree.sym.type.getThrownTypes(); |
732 |
|
733 if (!isInitialConstructor) |
|
734 firstadr = nextadr; |
|
735 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { |
|
736 JCVariableDecl def = l.head; |
|
737 scan(def); |
|
738 inits.incl(def.sym.adr); |
|
739 uninits.excl(def.sym.adr); |
|
740 } |
|
741 if (isInitialConstructor) |
|
742 caught = chk.union(caught, mthrown); |
|
743 else if ((tree.sym.flags() & (BLOCK | STATIC)) != BLOCK) |
|
744 caught = mthrown; |
|
745 // else we are in an instance initializer block; |
|
746 // leave caught unchanged. |
|
747 |
|
748 alive = true; |
|
749 scanStat(tree.body); |
|
750 |
|
751 if (alive && tree.sym.type.getReturnType().tag != VOID) |
|
752 log.error(TreeInfo.diagEndPos(tree.body), "missing.ret.stmt"); |
|
753 |
|
754 if (isInitialConstructor) { |
|
755 for (int i = firstadr; i < nextadr; i++) |
|
756 if (vars[i].owner == classDef.sym) |
|
757 checkInit(TreeInfo.diagEndPos(tree.body), vars[i]); |
|
758 } |
|
759 List<PendingExit> exits = pendingExits.toList(); |
|
760 pendingExits = new ListBuffer<PendingExit>(); |
|
761 while (exits.nonEmpty()) { |
|
762 PendingExit exit = exits.head; |
|
763 exits = exits.tail; |
|
764 if (exit.thrown == null) { |
|
765 Assert.check(exit.tree.hasTag(RETURN)); |
|
766 if (isInitialConstructor) { |
|
767 inits = exit.inits; |
|
768 for (int i = firstadr; i < nextadr; i++) |
|
769 checkInit(exit.tree.pos(), vars[i]); |
|
770 } |
|
771 } else { |
|
772 // uncaught throws will be reported later |
|
773 pendingExits.append(exit); |
|
774 } |
|
775 } |
|
776 } finally { |
|
777 inits = initsPrev; |
|
778 uninits = uninitsPrev; |
|
779 nextadr = nextadrPrev; |
|
780 firstadr = firstadrPrev; |
|
781 caught = caughtPrev; |
|
782 lint = lintPrev; |
|
783 } |
|
784 } |
|
785 |
|
786 public void visitVarDef(JCVariableDecl tree) { |
|
787 boolean track = trackable(tree.sym); |
|
788 if (track && tree.sym.owner.kind == MTH) newVar(tree.sym); |
|
789 if (tree.init != null) { |
|
790 Lint lintPrev = lint; |
523 Lint lintPrev = lint; |
|
524 |
791 lint = lint.augment(tree.sym.attributes_field); |
525 lint = lint.augment(tree.sym.attributes_field); |
792 try{ |
526 |
793 scanExpr(tree.init); |
527 Assert.check(pendingExits.isEmpty()); |
794 if (track) letInit(tree.pos(), tree.sym); |
528 |
|
529 try { |
|
530 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { |
|
531 JCVariableDecl def = l.head; |
|
532 scan(def); |
|
533 } |
|
534 if (TreeInfo.isInitialConstructor(tree)) |
|
535 caught = chk.union(caught, mthrown); |
|
536 else if ((tree.sym.flags() & (BLOCK | STATIC)) != BLOCK) |
|
537 caught = mthrown; |
|
538 // else we are in an instance initializer block; |
|
539 // leave caught unchanged. |
|
540 |
|
541 alive = true; |
|
542 scanStat(tree.body); |
|
543 |
|
544 if (alive && tree.sym.type.getReturnType().tag != VOID) |
|
545 log.error(TreeInfo.diagEndPos(tree.body), "missing.ret.stmt"); |
|
546 |
|
547 List<FlowPendingExit> exits = pendingExits.toList(); |
|
548 pendingExits = new ListBuffer<FlowPendingExit>(); |
|
549 while (exits.nonEmpty()) { |
|
550 FlowPendingExit exit = exits.head; |
|
551 exits = exits.tail; |
|
552 if (exit.thrown == null) { |
|
553 Assert.check(exit.tree.hasTag(RETURN)); |
|
554 } else { |
|
555 // uncaught throws will be reported later |
|
556 pendingExits.append(exit); |
|
557 } |
|
558 } |
795 } finally { |
559 } finally { |
|
560 caught = caughtPrev; |
796 lint = lintPrev; |
561 lint = lintPrev; |
797 } |
562 } |
798 } |
563 } |
799 } |
564 |
800 |
565 public void visitVarDef(JCVariableDecl tree) { |
801 public void visitBlock(JCBlock tree) { |
566 if (tree.init != null) { |
802 int nextadrPrev = nextadr; |
567 Lint lintPrev = lint; |
803 scanStats(tree.stats); |
568 lint = lint.augment(tree.sym.attributes_field); |
804 nextadr = nextadrPrev; |
569 try{ |
805 } |
570 scan(tree.init); |
806 |
571 } finally { |
807 public void visitDoLoop(JCDoWhileLoop tree) { |
572 lint = lintPrev; |
808 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
573 } |
809 boolean prevLoopPassTwo = loopPassTwo; |
574 } |
810 pendingExits = new ListBuffer<PendingExit>(); |
575 } |
811 int prevErrors = log.nerrors; |
576 |
812 do { |
577 public void visitBlock(JCBlock tree) { |
813 Bits uninitsEntry = uninits.dup(); |
578 scanStats(tree.stats); |
814 uninitsEntry.excludeFrom(nextadr); |
579 } |
|
580 |
|
581 public void visitDoLoop(JCDoWhileLoop tree) { |
|
582 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
|
583 pendingExits = new ListBuffer<FlowPendingExit>(); |
815 scanStat(tree.body); |
584 scanStat(tree.body); |
816 alive |= resolveContinues(tree); |
585 alive |= resolveContinues(tree); |
817 scanCond(tree.cond); |
586 scan(tree.cond); |
818 if (log.nerrors != prevErrors || |
587 alive = alive && !tree.cond.type.isTrue(); |
819 loopPassTwo || |
588 alive |= resolveBreaks(tree, prevPendingExits); |
820 uninitsEntry.dup().diffSet(uninitsWhenTrue).nextBit(firstadr)==-1) |
589 } |
821 break; |
590 |
822 inits = initsWhenTrue; |
591 public void visitWhileLoop(JCWhileLoop tree) { |
823 uninits = uninitsEntry.andSet(uninitsWhenTrue); |
592 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
824 loopPassTwo = true; |
593 pendingExits = new ListBuffer<FlowPendingExit>(); |
825 alive = true; |
594 scan(tree.cond); |
826 } while (true); |
|
827 loopPassTwo = prevLoopPassTwo; |
|
828 inits = initsWhenFalse; |
|
829 uninits = uninitsWhenFalse; |
|
830 alive = alive && !tree.cond.type.isTrue(); |
|
831 alive |= resolveBreaks(tree, prevPendingExits); |
|
832 } |
|
833 |
|
834 public void visitWhileLoop(JCWhileLoop tree) { |
|
835 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
|
836 boolean prevLoopPassTwo = loopPassTwo; |
|
837 Bits initsCond; |
|
838 Bits uninitsCond; |
|
839 pendingExits = new ListBuffer<PendingExit>(); |
|
840 int prevErrors = log.nerrors; |
|
841 do { |
|
842 Bits uninitsEntry = uninits.dup(); |
|
843 uninitsEntry.excludeFrom(nextadr); |
|
844 scanCond(tree.cond); |
|
845 initsCond = initsWhenFalse; |
|
846 uninitsCond = uninitsWhenFalse; |
|
847 inits = initsWhenTrue; |
|
848 uninits = uninitsWhenTrue; |
|
849 alive = !tree.cond.type.isFalse(); |
595 alive = !tree.cond.type.isFalse(); |
850 scanStat(tree.body); |
596 scanStat(tree.body); |
851 alive |= resolveContinues(tree); |
597 alive |= resolveContinues(tree); |
852 if (log.nerrors != prevErrors || |
598 alive = resolveBreaks(tree, prevPendingExits) || |
853 loopPassTwo || |
599 !tree.cond.type.isTrue(); |
854 uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1) |
600 } |
855 break; |
601 |
856 uninits = uninitsEntry.andSet(uninits); |
602 public void visitForLoop(JCForLoop tree) { |
857 loopPassTwo = true; |
603 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
|
604 scanStats(tree.init); |
|
605 pendingExits = new ListBuffer<FlowPendingExit>(); |
|
606 if (tree.cond != null) { |
|
607 scan(tree.cond); |
|
608 alive = !tree.cond.type.isFalse(); |
|
609 } else { |
|
610 alive = true; |
|
611 } |
|
612 scanStat(tree.body); |
|
613 alive |= resolveContinues(tree); |
|
614 scan(tree.step); |
|
615 alive = resolveBreaks(tree, prevPendingExits) || |
|
616 tree.cond != null && !tree.cond.type.isTrue(); |
|
617 } |
|
618 |
|
619 public void visitForeachLoop(JCEnhancedForLoop tree) { |
|
620 visitVarDef(tree.var); |
|
621 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
|
622 scan(tree.expr); |
|
623 pendingExits = new ListBuffer<FlowPendingExit>(); |
|
624 scanStat(tree.body); |
|
625 alive |= resolveContinues(tree); |
|
626 resolveBreaks(tree, prevPendingExits); |
858 alive = true; |
627 alive = true; |
859 } while (true); |
628 } |
860 loopPassTwo = prevLoopPassTwo; |
629 |
861 inits = initsCond; |
630 public void visitLabelled(JCLabeledStatement tree) { |
862 uninits = uninitsCond; |
631 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
863 alive = resolveBreaks(tree, prevPendingExits) || |
632 pendingExits = new ListBuffer<FlowPendingExit>(); |
864 !tree.cond.type.isTrue(); |
633 scanStat(tree.body); |
|
634 alive |= resolveBreaks(tree, prevPendingExits); |
|
635 } |
|
636 |
|
637 public void visitSwitch(JCSwitch tree) { |
|
638 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
|
639 pendingExits = new ListBuffer<FlowPendingExit>(); |
|
640 scan(tree.selector); |
|
641 boolean hasDefault = false; |
|
642 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { |
|
643 alive = true; |
|
644 JCCase c = l.head; |
|
645 if (c.pat == null) |
|
646 hasDefault = true; |
|
647 else |
|
648 scan(c.pat); |
|
649 scanStats(c.stats); |
|
650 // Warn about fall-through if lint switch fallthrough enabled. |
|
651 if (alive && |
|
652 lint.isEnabled(Lint.LintCategory.FALLTHROUGH) && |
|
653 c.stats.nonEmpty() && l.tail.nonEmpty()) |
|
654 log.warning(Lint.LintCategory.FALLTHROUGH, |
|
655 l.tail.head.pos(), |
|
656 "possible.fall-through.into.case"); |
|
657 } |
|
658 if (!hasDefault) { |
|
659 alive = true; |
|
660 } |
|
661 alive |= resolveBreaks(tree, prevPendingExits); |
|
662 } |
|
663 |
|
664 public void visitTry(JCTry tree) { |
|
665 List<Type> caughtPrev = caught; |
|
666 List<Type> thrownPrev = thrown; |
|
667 thrown = List.nil(); |
|
668 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { |
|
669 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ? |
|
670 ((JCTypeUnion)l.head.param.vartype).alternatives : |
|
671 List.of(l.head.param.vartype); |
|
672 for (JCExpression ct : subClauses) { |
|
673 caught = chk.incl(ct.type, caught); |
|
674 } |
|
675 } |
|
676 |
|
677 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
|
678 pendingExits = new ListBuffer<FlowPendingExit>(); |
|
679 for (JCTree resource : tree.resources) { |
|
680 if (resource instanceof JCVariableDecl) { |
|
681 JCVariableDecl vdecl = (JCVariableDecl) resource; |
|
682 visitVarDef(vdecl); |
|
683 } else if (resource instanceof JCExpression) { |
|
684 scan((JCExpression) resource); |
|
685 } else { |
|
686 throw new AssertionError(tree); // parser error |
|
687 } |
|
688 } |
|
689 for (JCTree resource : tree.resources) { |
|
690 List<Type> closeableSupertypes = resource.type.isCompound() ? |
|
691 types.interfaces(resource.type).prepend(types.supertype(resource.type)) : |
|
692 List.of(resource.type); |
|
693 for (Type sup : closeableSupertypes) { |
|
694 if (types.asSuper(sup, syms.autoCloseableType.tsym) != null) { |
|
695 Symbol closeMethod = rs.resolveQualifiedMethod(tree, |
|
696 attrEnv, |
|
697 sup, |
|
698 names.close, |
|
699 List.<Type>nil(), |
|
700 List.<Type>nil()); |
|
701 if (closeMethod.kind == MTH) { |
|
702 for (Type t : ((MethodSymbol)closeMethod).getThrownTypes()) { |
|
703 markThrown(resource, t); |
|
704 } |
|
705 } |
|
706 } |
|
707 } |
|
708 } |
|
709 scanStat(tree.body); |
|
710 List<Type> thrownInTry = allowImprovedCatchAnalysis ? |
|
711 chk.union(thrown, List.of(syms.runtimeExceptionType, syms.errorType)) : |
|
712 thrown; |
|
713 thrown = thrownPrev; |
|
714 caught = caughtPrev; |
|
715 boolean aliveEnd = alive; |
|
716 |
|
717 List<Type> caughtInTry = List.nil(); |
|
718 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { |
|
719 alive = true; |
|
720 JCVariableDecl param = l.head.param; |
|
721 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ? |
|
722 ((JCTypeUnion)l.head.param.vartype).alternatives : |
|
723 List.of(l.head.param.vartype); |
|
724 List<Type> ctypes = List.nil(); |
|
725 List<Type> rethrownTypes = chk.diff(thrownInTry, caughtInTry); |
|
726 for (JCExpression ct : subClauses) { |
|
727 Type exc = ct.type; |
|
728 if (exc != syms.unknownType) { |
|
729 ctypes = ctypes.append(exc); |
|
730 if (types.isSameType(exc, syms.objectType)) |
|
731 continue; |
|
732 checkCaughtType(l.head.pos(), exc, thrownInTry, caughtInTry); |
|
733 caughtInTry = chk.incl(exc, caughtInTry); |
|
734 } |
|
735 } |
|
736 scan(param); |
|
737 preciseRethrowTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes)); |
|
738 scanStat(l.head.body); |
|
739 preciseRethrowTypes.remove(param.sym); |
|
740 aliveEnd |= alive; |
|
741 } |
|
742 if (tree.finalizer != null) { |
|
743 List<Type> savedThrown = thrown; |
|
744 thrown = List.nil(); |
|
745 ListBuffer<FlowPendingExit> exits = pendingExits; |
|
746 pendingExits = prevPendingExits; |
|
747 alive = true; |
|
748 scanStat(tree.finalizer); |
|
749 if (!alive) { |
|
750 // discard exits and exceptions from try and finally |
|
751 thrown = chk.union(thrown, thrownPrev); |
|
752 if (lint.isEnabled(Lint.LintCategory.FINALLY)) { |
|
753 log.warning(Lint.LintCategory.FINALLY, |
|
754 TreeInfo.diagEndPos(tree.finalizer), |
|
755 "finally.cannot.complete"); |
|
756 } |
|
757 } else { |
|
758 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); |
|
759 thrown = chk.union(thrown, savedThrown); |
|
760 // FIX: this doesn't preserve source order of exits in catch |
|
761 // versus finally! |
|
762 while (exits.nonEmpty()) { |
|
763 pendingExits.append(exits.next()); |
|
764 } |
|
765 alive = aliveEnd; |
|
766 } |
|
767 tree.finallyCanCompleteNormally = alive; |
|
768 } else { |
|
769 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); |
|
770 alive = aliveEnd; |
|
771 ListBuffer<FlowPendingExit> exits = pendingExits; |
|
772 pendingExits = prevPendingExits; |
|
773 while (exits.nonEmpty()) pendingExits.append(exits.next()); |
|
774 } |
|
775 } |
|
776 |
|
777 @Override |
|
778 public void visitIf(JCIf tree) { |
|
779 scan(tree.cond); |
|
780 scanStat(tree.thenpart); |
|
781 if (tree.elsepart != null) { |
|
782 boolean aliveAfterThen = alive; |
|
783 alive = true; |
|
784 scanStat(tree.elsepart); |
|
785 alive = alive | aliveAfterThen; |
|
786 } else { |
|
787 alive = true; |
|
788 } |
|
789 } |
|
790 |
|
791 void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) { |
|
792 if (chk.subset(exc, caughtInTry)) { |
|
793 log.error(pos, "except.already.caught", exc); |
|
794 } else if (!chk.isUnchecked(pos, exc) && |
|
795 !isExceptionOrThrowable(exc) && |
|
796 !chk.intersects(exc, thrownInTry)) { |
|
797 log.error(pos, "except.never.thrown.in.try", exc); |
|
798 } else if (allowImprovedCatchAnalysis) { |
|
799 List<Type> catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry); |
|
800 // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an |
|
801 // unchecked exception, the result list would not be empty, as the augmented |
|
802 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked |
|
803 // exception, that would have been covered in the branch above |
|
804 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() && |
|
805 !isExceptionOrThrowable(exc)) { |
|
806 String key = catchableThrownTypes.length() == 1 ? |
|
807 "unreachable.catch" : |
|
808 "unreachable.catch.1"; |
|
809 log.warning(pos, key, catchableThrownTypes); |
|
810 } |
|
811 } |
|
812 } |
|
813 //where |
|
814 private boolean isExceptionOrThrowable(Type exc) { |
|
815 return exc.tsym == syms.throwableType.tsym || |
|
816 exc.tsym == syms.exceptionType.tsym; |
|
817 } |
|
818 |
|
819 public void visitBreak(JCBreak tree) { |
|
820 recordExit(tree, new FlowPendingExit(tree, null)); |
|
821 } |
|
822 |
|
823 public void visitContinue(JCContinue tree) { |
|
824 recordExit(tree, new FlowPendingExit(tree, null)); |
|
825 } |
|
826 |
|
827 public void visitReturn(JCReturn tree) { |
|
828 scan(tree.expr); |
|
829 // if not initial constructor, should markDead instead of recordExit |
|
830 recordExit(tree, new FlowPendingExit(tree, null)); |
|
831 } |
|
832 |
|
833 public void visitThrow(JCThrow tree) { |
|
834 scan(tree.expr); |
|
835 Symbol sym = TreeInfo.symbol(tree.expr); |
|
836 if (sym != null && |
|
837 sym.kind == VAR && |
|
838 (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 && |
|
839 preciseRethrowTypes.get(sym) != null && |
|
840 allowImprovedRethrowAnalysis) { |
|
841 for (Type t : preciseRethrowTypes.get(sym)) { |
|
842 markThrown(tree, t); |
|
843 } |
|
844 } |
|
845 else { |
|
846 markThrown(tree, tree.expr.type); |
|
847 } |
|
848 markDead(); |
|
849 } |
|
850 |
|
851 public void visitApply(JCMethodInvocation tree) { |
|
852 scan(tree.meth); |
|
853 scan(tree.args); |
|
854 for (List<Type> l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail) |
|
855 markThrown(tree, l.head); |
|
856 } |
|
857 |
|
858 public void visitNewClass(JCNewClass tree) { |
|
859 scan(tree.encl); |
|
860 scan(tree.args); |
|
861 // scan(tree.def); |
|
862 for (List<Type> l = tree.constructorType.getThrownTypes(); |
|
863 l.nonEmpty(); |
|
864 l = l.tail) { |
|
865 markThrown(tree, l.head); |
|
866 } |
|
867 List<Type> caughtPrev = caught; |
|
868 try { |
|
869 // If the new class expression defines an anonymous class, |
|
870 // analysis of the anonymous constructor may encounter thrown |
|
871 // types which are unsubstituted type variables. |
|
872 // However, since the constructor's actual thrown types have |
|
873 // already been marked as thrown, it is safe to simply include |
|
874 // each of the constructor's formal thrown types in the set of |
|
875 // 'caught/declared to be thrown' types, for the duration of |
|
876 // the class def analysis. |
|
877 if (tree.def != null) |
|
878 for (List<Type> l = tree.constructor.type.getThrownTypes(); |
|
879 l.nonEmpty(); |
|
880 l = l.tail) { |
|
881 caught = chk.incl(l.head, caught); |
|
882 } |
|
883 scan(tree.def); |
|
884 } |
|
885 finally { |
|
886 caught = caughtPrev; |
|
887 } |
|
888 } |
|
889 |
|
890 public void visitTopLevel(JCCompilationUnit tree) { |
|
891 // Do nothing for TopLevel since each class is visited individually |
|
892 } |
|
893 |
|
894 /************************************************************************** |
|
895 * main method |
|
896 *************************************************************************/ |
|
897 |
|
898 /** Perform definite assignment/unassignment analysis on a tree. |
|
899 */ |
|
900 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { |
|
901 try { |
|
902 attrEnv = env; |
|
903 JCTree tree = env.tree; |
|
904 Flow.this.make = make; |
|
905 pendingExits = new ListBuffer<FlowPendingExit>(); |
|
906 preciseRethrowTypes = new HashMap<Symbol, List<Type>>(); |
|
907 alive = true; |
|
908 this.thrown = this.caught = null; |
|
909 this.classDef = null; |
|
910 scan(tree); |
|
911 } finally { |
|
912 pendingExits = null; |
|
913 Flow.this.make = null; |
|
914 this.thrown = this.caught = null; |
|
915 this.classDef = null; |
|
916 } |
|
917 } |
865 } |
918 } |
866 |
919 |
867 public void visitForLoop(JCForLoop tree) { |
920 /** |
868 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
921 * This pass implements (i) definite assignment analysis, which ensures that |
869 boolean prevLoopPassTwo = loopPassTwo; |
922 * each variable is assigned when used and (ii) definite unassignment analysis, |
870 int nextadrPrev = nextadr; |
923 * which ensures that no final variable is assigned more than once. This visitor |
871 scanStats(tree.init); |
924 * depends on the results of the liveliness analyzer. |
872 Bits initsCond; |
925 */ |
873 Bits uninitsCond; |
926 class AssignAnalyzer extends BaseAnalyzer<AssignAnalyzer.AssignPendingExit> { |
874 pendingExits = new ListBuffer<PendingExit>(); |
927 |
875 int prevErrors = log.nerrors; |
928 /** The set of definitely assigned variables. |
876 do { |
929 */ |
877 Bits uninitsEntry = uninits.dup(); |
930 Bits inits; |
878 uninitsEntry.excludeFrom(nextadr); |
931 |
879 if (tree.cond != null) { |
932 /** The set of definitely unassigned variables. |
|
933 */ |
|
934 Bits uninits; |
|
935 |
|
936 /** The set of variables that are definitely unassigned everywhere |
|
937 * in current try block. This variable is maintained lazily; it is |
|
938 * updated only when something gets removed from uninits, |
|
939 * typically by being assigned in reachable code. To obtain the |
|
940 * correct set of variables which are definitely unassigned |
|
941 * anywhere in current try block, intersect uninitsTry and |
|
942 * uninits. |
|
943 */ |
|
944 Bits uninitsTry; |
|
945 |
|
946 /** When analyzing a condition, inits and uninits are null. |
|
947 * Instead we have: |
|
948 */ |
|
949 Bits initsWhenTrue; |
|
950 Bits initsWhenFalse; |
|
951 Bits uninitsWhenTrue; |
|
952 Bits uninitsWhenFalse; |
|
953 |
|
954 /** A mapping from addresses to variable symbols. |
|
955 */ |
|
956 VarSymbol[] vars; |
|
957 |
|
958 /** The current class being defined. |
|
959 */ |
|
960 JCClassDecl classDef; |
|
961 |
|
962 /** The first variable sequence number in this class definition. |
|
963 */ |
|
964 int firstadr; |
|
965 |
|
966 /** The next available variable sequence number. |
|
967 */ |
|
968 int nextadr; |
|
969 |
|
970 /** The list of unreferenced automatic resources. |
|
971 */ |
|
972 Scope unrefdResources; |
|
973 |
|
974 /** Set when processing a loop body the second time for DU analysis. */ |
|
975 boolean loopPassTwo = false; |
|
976 |
|
977 class AssignPendingExit extends BaseAnalyzer.PendingExit { |
|
978 |
|
979 Bits exit_inits; |
|
980 Bits exit_uninits; |
|
981 |
|
982 AssignPendingExit(JCTree tree, Bits inits, Bits uninits) { |
|
983 super(tree); |
|
984 this.exit_inits = inits.dup(); |
|
985 this.exit_uninits = uninits.dup(); |
|
986 } |
|
987 |
|
988 void resolveJump() { |
|
989 inits.andSet(exit_inits); |
|
990 uninits.andSet(exit_uninits); |
|
991 } |
|
992 } |
|
993 |
|
994 @Override |
|
995 void markDead() { |
|
996 inits.inclRange(firstadr, nextadr); |
|
997 uninits.inclRange(firstadr, nextadr); |
|
998 } |
|
999 |
|
1000 /*-------------- Processing variables ----------------------*/ |
|
1001 |
|
1002 /** Do we need to track init/uninit state of this symbol? |
|
1003 * I.e. is symbol either a local or a blank final variable? |
|
1004 */ |
|
1005 boolean trackable(VarSymbol sym) { |
|
1006 return |
|
1007 (sym.owner.kind == MTH || |
|
1008 ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL && |
|
1009 classDef.sym.isEnclosedBy((ClassSymbol)sym.owner))); |
|
1010 } |
|
1011 |
|
1012 /** Initialize new trackable variable by setting its address field |
|
1013 * to the next available sequence number and entering it under that |
|
1014 * index into the vars array. |
|
1015 */ |
|
1016 void newVar(VarSymbol sym) { |
|
1017 if (nextadr == vars.length) { |
|
1018 VarSymbol[] newvars = new VarSymbol[nextadr * 2]; |
|
1019 System.arraycopy(vars, 0, newvars, 0, nextadr); |
|
1020 vars = newvars; |
|
1021 } |
|
1022 sym.adr = nextadr; |
|
1023 vars[nextadr] = sym; |
|
1024 inits.excl(nextadr); |
|
1025 uninits.incl(nextadr); |
|
1026 nextadr++; |
|
1027 } |
|
1028 |
|
1029 /** Record an initialization of a trackable variable. |
|
1030 */ |
|
1031 void letInit(DiagnosticPosition pos, VarSymbol sym) { |
|
1032 if (sym.adr >= firstadr && trackable(sym)) { |
|
1033 if ((sym.flags() & FINAL) != 0) { |
|
1034 if ((sym.flags() & PARAMETER) != 0) { |
|
1035 if ((sym.flags() & UNION) != 0) { //multi-catch parameter |
|
1036 log.error(pos, "multicatch.parameter.may.not.be.assigned", |
|
1037 sym); |
|
1038 } |
|
1039 else { |
|
1040 log.error(pos, "final.parameter.may.not.be.assigned", |
|
1041 sym); |
|
1042 } |
|
1043 } else if (!uninits.isMember(sym.adr)) { |
|
1044 log.error(pos, |
|
1045 loopPassTwo |
|
1046 ? "var.might.be.assigned.in.loop" |
|
1047 : "var.might.already.be.assigned", |
|
1048 sym); |
|
1049 } else if (!inits.isMember(sym.adr)) { |
|
1050 // reachable assignment |
|
1051 uninits.excl(sym.adr); |
|
1052 uninitsTry.excl(sym.adr); |
|
1053 } else { |
|
1054 //log.rawWarning(pos, "unreachable assignment");//DEBUG |
|
1055 uninits.excl(sym.adr); |
|
1056 } |
|
1057 } |
|
1058 inits.incl(sym.adr); |
|
1059 } else if ((sym.flags() & FINAL) != 0) { |
|
1060 log.error(pos, "var.might.already.be.assigned", sym); |
|
1061 } |
|
1062 } |
|
1063 |
|
1064 /** If tree is either a simple name or of the form this.name or |
|
1065 * C.this.name, and tree represents a trackable variable, |
|
1066 * record an initialization of the variable. |
|
1067 */ |
|
1068 void letInit(JCTree tree) { |
|
1069 tree = TreeInfo.skipParens(tree); |
|
1070 if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { |
|
1071 Symbol sym = TreeInfo.symbol(tree); |
|
1072 if (sym.kind == VAR) { |
|
1073 letInit(tree.pos(), (VarSymbol)sym); |
|
1074 } |
|
1075 } |
|
1076 } |
|
1077 |
|
1078 /** Check that trackable variable is initialized. |
|
1079 */ |
|
1080 void checkInit(DiagnosticPosition pos, VarSymbol sym) { |
|
1081 if ((sym.adr >= firstadr || sym.owner.kind != TYP) && |
|
1082 trackable(sym) && |
|
1083 !inits.isMember(sym.adr)) { |
|
1084 log.error(pos, "var.might.not.have.been.initialized", |
|
1085 sym); |
|
1086 inits.incl(sym.adr); |
|
1087 } |
|
1088 } |
|
1089 |
|
1090 /** Split (duplicate) inits/uninits into WhenTrue/WhenFalse sets |
|
1091 */ |
|
1092 void split(boolean setToNull) { |
|
1093 initsWhenFalse = inits.dup(); |
|
1094 uninitsWhenFalse = uninits.dup(); |
|
1095 initsWhenTrue = inits; |
|
1096 uninitsWhenTrue = uninits; |
|
1097 if (setToNull) |
|
1098 inits = uninits = null; |
|
1099 } |
|
1100 |
|
1101 /** Merge (intersect) inits/uninits from WhenTrue/WhenFalse sets. |
|
1102 */ |
|
1103 void merge() { |
|
1104 inits = initsWhenFalse.andSet(initsWhenTrue); |
|
1105 uninits = uninitsWhenFalse.andSet(uninitsWhenTrue); |
|
1106 } |
|
1107 |
|
1108 /* ************************************************************************ |
|
1109 * Visitor methods for statements and definitions |
|
1110 *************************************************************************/ |
|
1111 |
|
1112 /** Analyze an expression. Make sure to set (un)inits rather than |
|
1113 * (un)initsWhenTrue(WhenFalse) on exit. |
|
1114 */ |
|
1115 void scanExpr(JCTree tree) { |
|
1116 if (tree != null) { |
|
1117 scan(tree); |
|
1118 if (inits == null) merge(); |
|
1119 } |
|
1120 } |
|
1121 |
|
1122 /** Analyze a list of expressions. |
|
1123 */ |
|
1124 void scanExprs(List<? extends JCExpression> trees) { |
|
1125 if (trees != null) |
|
1126 for (List<? extends JCExpression> l = trees; l.nonEmpty(); l = l.tail) |
|
1127 scanExpr(l.head); |
|
1128 } |
|
1129 |
|
1130 /** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse) |
|
1131 * rather than (un)inits on exit. |
|
1132 */ |
|
1133 void scanCond(JCTree tree) { |
|
1134 if (tree.type.isFalse()) { |
|
1135 if (inits == null) merge(); |
|
1136 initsWhenTrue = inits.dup(); |
|
1137 initsWhenTrue.inclRange(firstadr, nextadr); |
|
1138 uninitsWhenTrue = uninits.dup(); |
|
1139 uninitsWhenTrue.inclRange(firstadr, nextadr); |
|
1140 initsWhenFalse = inits; |
|
1141 uninitsWhenFalse = uninits; |
|
1142 } else if (tree.type.isTrue()) { |
|
1143 if (inits == null) merge(); |
|
1144 initsWhenFalse = inits.dup(); |
|
1145 initsWhenFalse.inclRange(firstadr, nextadr); |
|
1146 uninitsWhenFalse = uninits.dup(); |
|
1147 uninitsWhenFalse.inclRange(firstadr, nextadr); |
|
1148 initsWhenTrue = inits; |
|
1149 uninitsWhenTrue = uninits; |
|
1150 } else { |
|
1151 scan(tree); |
|
1152 if (inits != null) |
|
1153 split(tree.type != syms.unknownType); |
|
1154 } |
|
1155 if (tree.type != syms.unknownType) |
|
1156 inits = uninits = null; |
|
1157 } |
|
1158 |
|
1159 /* ------------ Visitor methods for various sorts of trees -------------*/ |
|
1160 |
|
1161 public void visitClassDef(JCClassDecl tree) { |
|
1162 if (tree.sym == null) return; |
|
1163 |
|
1164 JCClassDecl classDefPrev = classDef; |
|
1165 int firstadrPrev = firstadr; |
|
1166 int nextadrPrev = nextadr; |
|
1167 ListBuffer<AssignPendingExit> pendingExitsPrev = pendingExits; |
|
1168 Lint lintPrev = lint; |
|
1169 |
|
1170 pendingExits = new ListBuffer<AssignPendingExit>(); |
|
1171 if (tree.name != names.empty) { |
|
1172 firstadr = nextadr; |
|
1173 } |
|
1174 classDef = tree; |
|
1175 lint = lint.augment(tree.sym.attributes_field); |
|
1176 |
|
1177 try { |
|
1178 // define all the static fields |
|
1179 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
|
1180 if (l.head.hasTag(VARDEF)) { |
|
1181 JCVariableDecl def = (JCVariableDecl)l.head; |
|
1182 if ((def.mods.flags & STATIC) != 0) { |
|
1183 VarSymbol sym = def.sym; |
|
1184 if (trackable(sym)) |
|
1185 newVar(sym); |
|
1186 } |
|
1187 } |
|
1188 } |
|
1189 |
|
1190 // process all the static initializers |
|
1191 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
|
1192 if (!l.head.hasTag(METHODDEF) && |
|
1193 (TreeInfo.flags(l.head) & STATIC) != 0) { |
|
1194 scan(l.head); |
|
1195 } |
|
1196 } |
|
1197 |
|
1198 // define all the instance fields |
|
1199 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
|
1200 if (l.head.hasTag(VARDEF)) { |
|
1201 JCVariableDecl def = (JCVariableDecl)l.head; |
|
1202 if ((def.mods.flags & STATIC) == 0) { |
|
1203 VarSymbol sym = def.sym; |
|
1204 if (trackable(sym)) |
|
1205 newVar(sym); |
|
1206 } |
|
1207 } |
|
1208 } |
|
1209 |
|
1210 // process all the instance initializers |
|
1211 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
|
1212 if (!l.head.hasTag(METHODDEF) && |
|
1213 (TreeInfo.flags(l.head) & STATIC) == 0) { |
|
1214 scan(l.head); |
|
1215 } |
|
1216 } |
|
1217 |
|
1218 // process all the methods |
|
1219 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
|
1220 if (l.head.hasTag(METHODDEF)) { |
|
1221 scan(l.head); |
|
1222 } |
|
1223 } |
|
1224 } finally { |
|
1225 pendingExits = pendingExitsPrev; |
|
1226 nextadr = nextadrPrev; |
|
1227 firstadr = firstadrPrev; |
|
1228 classDef = classDefPrev; |
|
1229 lint = lintPrev; |
|
1230 } |
|
1231 } |
|
1232 |
|
1233 public void visitMethodDef(JCMethodDecl tree) { |
|
1234 if (tree.body == null) return; |
|
1235 |
|
1236 Bits initsPrev = inits.dup(); |
|
1237 Bits uninitsPrev = uninits.dup(); |
|
1238 int nextadrPrev = nextadr; |
|
1239 int firstadrPrev = firstadr; |
|
1240 Lint lintPrev = lint; |
|
1241 |
|
1242 lint = lint.augment(tree.sym.attributes_field); |
|
1243 |
|
1244 Assert.check(pendingExits.isEmpty()); |
|
1245 |
|
1246 try { |
|
1247 boolean isInitialConstructor = |
|
1248 TreeInfo.isInitialConstructor(tree); |
|
1249 |
|
1250 if (!isInitialConstructor) |
|
1251 firstadr = nextadr; |
|
1252 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { |
|
1253 JCVariableDecl def = l.head; |
|
1254 scan(def); |
|
1255 inits.incl(def.sym.adr); |
|
1256 uninits.excl(def.sym.adr); |
|
1257 } |
|
1258 // else we are in an instance initializer block; |
|
1259 // leave caught unchanged. |
|
1260 scan(tree.body); |
|
1261 |
|
1262 if (isInitialConstructor) { |
|
1263 for (int i = firstadr; i < nextadr; i++) |
|
1264 if (vars[i].owner == classDef.sym) |
|
1265 checkInit(TreeInfo.diagEndPos(tree.body), vars[i]); |
|
1266 } |
|
1267 List<AssignPendingExit> exits = pendingExits.toList(); |
|
1268 pendingExits = new ListBuffer<AssignPendingExit>(); |
|
1269 while (exits.nonEmpty()) { |
|
1270 AssignPendingExit exit = exits.head; |
|
1271 exits = exits.tail; |
|
1272 Assert.check(exit.tree.hasTag(RETURN), exit.tree); |
|
1273 if (isInitialConstructor) { |
|
1274 inits = exit.exit_inits; |
|
1275 for (int i = firstadr; i < nextadr; i++) |
|
1276 checkInit(exit.tree.pos(), vars[i]); |
|
1277 } |
|
1278 } |
|
1279 } finally { |
|
1280 inits = initsPrev; |
|
1281 uninits = uninitsPrev; |
|
1282 nextadr = nextadrPrev; |
|
1283 firstadr = firstadrPrev; |
|
1284 lint = lintPrev; |
|
1285 } |
|
1286 } |
|
1287 |
|
1288 public void visitVarDef(JCVariableDecl tree) { |
|
1289 boolean track = trackable(tree.sym); |
|
1290 if (track && tree.sym.owner.kind == MTH) newVar(tree.sym); |
|
1291 if (tree.init != null) { |
|
1292 Lint lintPrev = lint; |
|
1293 lint = lint.augment(tree.sym.attributes_field); |
|
1294 try{ |
|
1295 scanExpr(tree.init); |
|
1296 if (track) letInit(tree.pos(), tree.sym); |
|
1297 } finally { |
|
1298 lint = lintPrev; |
|
1299 } |
|
1300 } |
|
1301 } |
|
1302 |
|
1303 public void visitBlock(JCBlock tree) { |
|
1304 int nextadrPrev = nextadr; |
|
1305 scan(tree.stats); |
|
1306 nextadr = nextadrPrev; |
|
1307 } |
|
1308 |
|
1309 public void visitDoLoop(JCDoWhileLoop tree) { |
|
1310 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
|
1311 boolean prevLoopPassTwo = loopPassTwo; |
|
1312 pendingExits = new ListBuffer<AssignPendingExit>(); |
|
1313 int prevErrors = log.nerrors; |
|
1314 do { |
|
1315 Bits uninitsEntry = uninits.dup(); |
|
1316 uninitsEntry.excludeFrom(nextadr); |
|
1317 scan(tree.body); |
|
1318 resolveContinues(tree); |
|
1319 scanCond(tree.cond); |
|
1320 if (log.nerrors != prevErrors || |
|
1321 loopPassTwo || |
|
1322 uninitsEntry.dup().diffSet(uninitsWhenTrue).nextBit(firstadr)==-1) |
|
1323 break; |
|
1324 inits = initsWhenTrue; |
|
1325 uninits = uninitsEntry.andSet(uninitsWhenTrue); |
|
1326 loopPassTwo = true; |
|
1327 } while (true); |
|
1328 loopPassTwo = prevLoopPassTwo; |
|
1329 inits = initsWhenFalse; |
|
1330 uninits = uninitsWhenFalse; |
|
1331 resolveBreaks(tree, prevPendingExits); |
|
1332 } |
|
1333 |
|
1334 public void visitWhileLoop(JCWhileLoop tree) { |
|
1335 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
|
1336 boolean prevLoopPassTwo = loopPassTwo; |
|
1337 Bits initsCond; |
|
1338 Bits uninitsCond; |
|
1339 pendingExits = new ListBuffer<AssignPendingExit>(); |
|
1340 int prevErrors = log.nerrors; |
|
1341 do { |
|
1342 Bits uninitsEntry = uninits.dup(); |
|
1343 uninitsEntry.excludeFrom(nextadr); |
880 scanCond(tree.cond); |
1344 scanCond(tree.cond); |
881 initsCond = initsWhenFalse; |
1345 initsCond = initsWhenFalse; |
882 uninitsCond = uninitsWhenFalse; |
1346 uninitsCond = uninitsWhenFalse; |
883 inits = initsWhenTrue; |
1347 inits = initsWhenTrue; |
884 uninits = uninitsWhenTrue; |
1348 uninits = uninitsWhenTrue; |
885 alive = !tree.cond.type.isFalse(); |
1349 scan(tree.body); |
|
1350 resolveContinues(tree); |
|
1351 if (log.nerrors != prevErrors || |
|
1352 loopPassTwo || |
|
1353 uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1) |
|
1354 break; |
|
1355 uninits = uninitsEntry.andSet(uninits); |
|
1356 loopPassTwo = true; |
|
1357 } while (true); |
|
1358 loopPassTwo = prevLoopPassTwo; |
|
1359 inits = initsCond; |
|
1360 uninits = uninitsCond; |
|
1361 resolveBreaks(tree, prevPendingExits); |
|
1362 } |
|
1363 |
|
1364 public void visitForLoop(JCForLoop tree) { |
|
1365 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
|
1366 boolean prevLoopPassTwo = loopPassTwo; |
|
1367 int nextadrPrev = nextadr; |
|
1368 scan(tree.init); |
|
1369 Bits initsCond; |
|
1370 Bits uninitsCond; |
|
1371 pendingExits = new ListBuffer<AssignPendingExit>(); |
|
1372 int prevErrors = log.nerrors; |
|
1373 do { |
|
1374 Bits uninitsEntry = uninits.dup(); |
|
1375 uninitsEntry.excludeFrom(nextadr); |
|
1376 if (tree.cond != null) { |
|
1377 scanCond(tree.cond); |
|
1378 initsCond = initsWhenFalse; |
|
1379 uninitsCond = uninitsWhenFalse; |
|
1380 inits = initsWhenTrue; |
|
1381 uninits = uninitsWhenTrue; |
|
1382 } else { |
|
1383 initsCond = inits.dup(); |
|
1384 initsCond.inclRange(firstadr, nextadr); |
|
1385 uninitsCond = uninits.dup(); |
|
1386 uninitsCond.inclRange(firstadr, nextadr); |
|
1387 } |
|
1388 scan(tree.body); |
|
1389 resolveContinues(tree); |
|
1390 scan(tree.step); |
|
1391 if (log.nerrors != prevErrors || |
|
1392 loopPassTwo || |
|
1393 uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1) |
|
1394 break; |
|
1395 uninits = uninitsEntry.andSet(uninits); |
|
1396 loopPassTwo = true; |
|
1397 } while (true); |
|
1398 loopPassTwo = prevLoopPassTwo; |
|
1399 inits = initsCond; |
|
1400 uninits = uninitsCond; |
|
1401 resolveBreaks(tree, prevPendingExits); |
|
1402 nextadr = nextadrPrev; |
|
1403 } |
|
1404 |
|
1405 public void visitForeachLoop(JCEnhancedForLoop tree) { |
|
1406 visitVarDef(tree.var); |
|
1407 |
|
1408 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
|
1409 boolean prevLoopPassTwo = loopPassTwo; |
|
1410 int nextadrPrev = nextadr; |
|
1411 scan(tree.expr); |
|
1412 Bits initsStart = inits.dup(); |
|
1413 Bits uninitsStart = uninits.dup(); |
|
1414 |
|
1415 letInit(tree.pos(), tree.var.sym); |
|
1416 pendingExits = new ListBuffer<AssignPendingExit>(); |
|
1417 int prevErrors = log.nerrors; |
|
1418 do { |
|
1419 Bits uninitsEntry = uninits.dup(); |
|
1420 uninitsEntry.excludeFrom(nextadr); |
|
1421 scan(tree.body); |
|
1422 resolveContinues(tree); |
|
1423 if (log.nerrors != prevErrors || |
|
1424 loopPassTwo || |
|
1425 uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1) |
|
1426 break; |
|
1427 uninits = uninitsEntry.andSet(uninits); |
|
1428 loopPassTwo = true; |
|
1429 } while (true); |
|
1430 loopPassTwo = prevLoopPassTwo; |
|
1431 inits = initsStart; |
|
1432 uninits = uninitsStart.andSet(uninits); |
|
1433 resolveBreaks(tree, prevPendingExits); |
|
1434 nextadr = nextadrPrev; |
|
1435 } |
|
1436 |
|
1437 public void visitLabelled(JCLabeledStatement tree) { |
|
1438 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
|
1439 pendingExits = new ListBuffer<AssignPendingExit>(); |
|
1440 scan(tree.body); |
|
1441 resolveBreaks(tree, prevPendingExits); |
|
1442 } |
|
1443 |
|
1444 public void visitSwitch(JCSwitch tree) { |
|
1445 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
|
1446 pendingExits = new ListBuffer<AssignPendingExit>(); |
|
1447 int nextadrPrev = nextadr; |
|
1448 scanExpr(tree.selector); |
|
1449 Bits initsSwitch = inits; |
|
1450 Bits uninitsSwitch = uninits.dup(); |
|
1451 boolean hasDefault = false; |
|
1452 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { |
|
1453 inits = initsSwitch.dup(); |
|
1454 uninits = uninits.andSet(uninitsSwitch); |
|
1455 JCCase c = l.head; |
|
1456 if (c.pat == null) |
|
1457 hasDefault = true; |
|
1458 else |
|
1459 scanExpr(c.pat); |
|
1460 scan(c.stats); |
|
1461 addVars(c.stats, initsSwitch, uninitsSwitch); |
|
1462 // Warn about fall-through if lint switch fallthrough enabled. |
|
1463 } |
|
1464 if (!hasDefault) { |
|
1465 inits.andSet(initsSwitch); |
|
1466 } |
|
1467 resolveBreaks(tree, prevPendingExits); |
|
1468 nextadr = nextadrPrev; |
|
1469 } |
|
1470 // where |
|
1471 /** Add any variables defined in stats to inits and uninits. */ |
|
1472 private void addVars(List<JCStatement> stats, Bits inits, |
|
1473 Bits uninits) { |
|
1474 for (;stats.nonEmpty(); stats = stats.tail) { |
|
1475 JCTree stat = stats.head; |
|
1476 if (stat.hasTag(VARDEF)) { |
|
1477 int adr = ((JCVariableDecl) stat).sym.adr; |
|
1478 inits.excl(adr); |
|
1479 uninits.incl(adr); |
|
1480 } |
|
1481 } |
|
1482 } |
|
1483 |
|
1484 public void visitTry(JCTry tree) { |
|
1485 ListBuffer<JCVariableDecl> resourceVarDecls = ListBuffer.lb(); |
|
1486 Bits uninitsTryPrev = uninitsTry; |
|
1487 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
|
1488 pendingExits = new ListBuffer<AssignPendingExit>(); |
|
1489 Bits initsTry = inits.dup(); |
|
1490 uninitsTry = uninits.dup(); |
|
1491 for (JCTree resource : tree.resources) { |
|
1492 if (resource instanceof JCVariableDecl) { |
|
1493 JCVariableDecl vdecl = (JCVariableDecl) resource; |
|
1494 visitVarDef(vdecl); |
|
1495 unrefdResources.enter(vdecl.sym); |
|
1496 resourceVarDecls.append(vdecl); |
|
1497 } else if (resource instanceof JCExpression) { |
|
1498 scanExpr((JCExpression) resource); |
|
1499 } else { |
|
1500 throw new AssertionError(tree); // parser error |
|
1501 } |
|
1502 } |
|
1503 scan(tree.body); |
|
1504 uninitsTry.andSet(uninits); |
|
1505 Bits initsEnd = inits; |
|
1506 Bits uninitsEnd = uninits; |
|
1507 int nextadrCatch = nextadr; |
|
1508 |
|
1509 if (!resourceVarDecls.isEmpty() && |
|
1510 lint.isEnabled(Lint.LintCategory.TRY)) { |
|
1511 for (JCVariableDecl resVar : resourceVarDecls) { |
|
1512 if (unrefdResources.includes(resVar.sym)) { |
|
1513 log.warning(Lint.LintCategory.TRY, resVar.pos(), |
|
1514 "try.resource.not.referenced", resVar.sym); |
|
1515 unrefdResources.remove(resVar.sym); |
|
1516 } |
|
1517 } |
|
1518 } |
|
1519 |
|
1520 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { |
|
1521 JCVariableDecl param = l.head.param; |
|
1522 inits = initsTry.dup(); |
|
1523 uninits = uninitsTry.dup(); |
|
1524 scan(param); |
|
1525 inits.incl(param.sym.adr); |
|
1526 uninits.excl(param.sym.adr); |
|
1527 scan(l.head.body); |
|
1528 initsEnd.andSet(inits); |
|
1529 uninitsEnd.andSet(uninits); |
|
1530 nextadr = nextadrCatch; |
|
1531 } |
|
1532 if (tree.finalizer != null) { |
|
1533 inits = initsTry.dup(); |
|
1534 uninits = uninitsTry.dup(); |
|
1535 ListBuffer<AssignPendingExit> exits = pendingExits; |
|
1536 pendingExits = prevPendingExits; |
|
1537 scan(tree.finalizer); |
|
1538 if (!tree.finallyCanCompleteNormally) { |
|
1539 // discard exits and exceptions from try and finally |
|
1540 } else { |
|
1541 uninits.andSet(uninitsEnd); |
|
1542 // FIX: this doesn't preserve source order of exits in catch |
|
1543 // versus finally! |
|
1544 while (exits.nonEmpty()) { |
|
1545 AssignPendingExit exit = exits.next(); |
|
1546 if (exit.exit_inits != null) { |
|
1547 exit.exit_inits.orSet(inits); |
|
1548 exit.exit_uninits.andSet(uninits); |
|
1549 } |
|
1550 pendingExits.append(exit); |
|
1551 } |
|
1552 inits.orSet(initsEnd); |
|
1553 } |
886 } else { |
1554 } else { |
887 initsCond = inits.dup(); |
1555 inits = initsEnd; |
888 initsCond.inclRange(firstadr, nextadr); |
1556 uninits = uninitsEnd; |
889 uninitsCond = uninits.dup(); |
1557 ListBuffer<AssignPendingExit> exits = pendingExits; |
890 uninitsCond.inclRange(firstadr, nextadr); |
1558 pendingExits = prevPendingExits; |
891 alive = true; |
1559 while (exits.nonEmpty()) pendingExits.append(exits.next()); |
892 } |
1560 } |
893 scanStat(tree.body); |
1561 uninitsTry.andSet(uninitsTryPrev).andSet(uninits); |
894 alive |= resolveContinues(tree); |
1562 } |
895 scan(tree.step); |
1563 |
896 if (log.nerrors != prevErrors || |
1564 public void visitConditional(JCConditional tree) { |
897 loopPassTwo || |
1565 scanCond(tree.cond); |
898 uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1) |
1566 Bits initsBeforeElse = initsWhenFalse; |
899 break; |
1567 Bits uninitsBeforeElse = uninitsWhenFalse; |
900 uninits = uninitsEntry.andSet(uninits); |
|
901 loopPassTwo = true; |
|
902 alive = true; |
|
903 } while (true); |
|
904 loopPassTwo = prevLoopPassTwo; |
|
905 inits = initsCond; |
|
906 uninits = uninitsCond; |
|
907 alive = resolveBreaks(tree, prevPendingExits) || |
|
908 tree.cond != null && !tree.cond.type.isTrue(); |
|
909 nextadr = nextadrPrev; |
|
910 } |
|
911 |
|
912 public void visitForeachLoop(JCEnhancedForLoop tree) { |
|
913 visitVarDef(tree.var); |
|
914 |
|
915 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
|
916 boolean prevLoopPassTwo = loopPassTwo; |
|
917 int nextadrPrev = nextadr; |
|
918 scan(tree.expr); |
|
919 Bits initsStart = inits.dup(); |
|
920 Bits uninitsStart = uninits.dup(); |
|
921 |
|
922 letInit(tree.pos(), tree.var.sym); |
|
923 pendingExits = new ListBuffer<PendingExit>(); |
|
924 int prevErrors = log.nerrors; |
|
925 do { |
|
926 Bits uninitsEntry = uninits.dup(); |
|
927 uninitsEntry.excludeFrom(nextadr); |
|
928 scanStat(tree.body); |
|
929 alive |= resolveContinues(tree); |
|
930 if (log.nerrors != prevErrors || |
|
931 loopPassTwo || |
|
932 uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1) |
|
933 break; |
|
934 uninits = uninitsEntry.andSet(uninits); |
|
935 loopPassTwo = true; |
|
936 alive = true; |
|
937 } while (true); |
|
938 loopPassTwo = prevLoopPassTwo; |
|
939 inits = initsStart; |
|
940 uninits = uninitsStart.andSet(uninits); |
|
941 resolveBreaks(tree, prevPendingExits); |
|
942 alive = true; |
|
943 nextadr = nextadrPrev; |
|
944 } |
|
945 |
|
946 public void visitLabelled(JCLabeledStatement tree) { |
|
947 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
|
948 pendingExits = new ListBuffer<PendingExit>(); |
|
949 scanStat(tree.body); |
|
950 alive |= resolveBreaks(tree, prevPendingExits); |
|
951 } |
|
952 |
|
953 public void visitSwitch(JCSwitch tree) { |
|
954 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
|
955 pendingExits = new ListBuffer<PendingExit>(); |
|
956 int nextadrPrev = nextadr; |
|
957 scanExpr(tree.selector); |
|
958 Bits initsSwitch = inits; |
|
959 Bits uninitsSwitch = uninits.dup(); |
|
960 boolean hasDefault = false; |
|
961 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { |
|
962 alive = true; |
|
963 inits = initsSwitch.dup(); |
|
964 uninits = uninits.andSet(uninitsSwitch); |
|
965 JCCase c = l.head; |
|
966 if (c.pat == null) |
|
967 hasDefault = true; |
|
968 else |
|
969 scanExpr(c.pat); |
|
970 scanStats(c.stats); |
|
971 addVars(c.stats, initsSwitch, uninitsSwitch); |
|
972 // Warn about fall-through if lint switch fallthrough enabled. |
|
973 if (!loopPassTwo && |
|
974 alive && |
|
975 lint.isEnabled(Lint.LintCategory.FALLTHROUGH) && |
|
976 c.stats.nonEmpty() && l.tail.nonEmpty()) |
|
977 log.warning(Lint.LintCategory.FALLTHROUGH, |
|
978 l.tail.head.pos(), |
|
979 "possible.fall-through.into.case"); |
|
980 } |
|
981 if (!hasDefault) { |
|
982 inits.andSet(initsSwitch); |
|
983 alive = true; |
|
984 } |
|
985 alive |= resolveBreaks(tree, prevPendingExits); |
|
986 nextadr = nextadrPrev; |
|
987 } |
|
988 // where |
|
989 /** Add any variables defined in stats to inits and uninits. */ |
|
990 private static void addVars(List<JCStatement> stats, Bits inits, |
|
991 Bits uninits) { |
|
992 for (;stats.nonEmpty(); stats = stats.tail) { |
|
993 JCTree stat = stats.head; |
|
994 if (stat.hasTag(VARDEF)) { |
|
995 int adr = ((JCVariableDecl) stat).sym.adr; |
|
996 inits.excl(adr); |
|
997 uninits.incl(adr); |
|
998 } |
|
999 } |
|
1000 } |
|
1001 |
|
1002 public void visitTry(JCTry tree) { |
|
1003 List<Type> caughtPrev = caught; |
|
1004 List<Type> thrownPrev = thrown; |
|
1005 thrown = List.nil(); |
|
1006 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { |
|
1007 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ? |
|
1008 ((JCTypeUnion)l.head.param.vartype).alternatives : |
|
1009 List.of(l.head.param.vartype); |
|
1010 for (JCExpression ct : subClauses) { |
|
1011 caught = chk.incl(ct.type, caught); |
|
1012 } |
|
1013 } |
|
1014 ListBuffer<JCVariableDecl> resourceVarDecls = ListBuffer.lb(); |
|
1015 Bits uninitsTryPrev = uninitsTry; |
|
1016 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
|
1017 pendingExits = new ListBuffer<PendingExit>(); |
|
1018 Bits initsTry = inits.dup(); |
|
1019 uninitsTry = uninits.dup(); |
|
1020 for (JCTree resource : tree.resources) { |
|
1021 if (resource instanceof JCVariableDecl) { |
|
1022 JCVariableDecl vdecl = (JCVariableDecl) resource; |
|
1023 visitVarDef(vdecl); |
|
1024 unrefdResources.enter(vdecl.sym); |
|
1025 resourceVarDecls.append(vdecl); |
|
1026 } else if (resource instanceof JCExpression) { |
|
1027 scanExpr((JCExpression) resource); |
|
1028 } else { |
|
1029 throw new AssertionError(tree); // parser error |
|
1030 } |
|
1031 } |
|
1032 for (JCTree resource : tree.resources) { |
|
1033 List<Type> closeableSupertypes = resource.type.isCompound() ? |
|
1034 types.interfaces(resource.type).prepend(types.supertype(resource.type)) : |
|
1035 List.of(resource.type); |
|
1036 for (Type sup : closeableSupertypes) { |
|
1037 if (types.asSuper(sup, syms.autoCloseableType.tsym) != null) { |
|
1038 Symbol closeMethod = rs.resolveQualifiedMethod(tree, |
|
1039 attrEnv, |
|
1040 sup, |
|
1041 names.close, |
|
1042 List.<Type>nil(), |
|
1043 List.<Type>nil()); |
|
1044 if (closeMethod.kind == MTH) { |
|
1045 for (Type t : ((MethodSymbol)closeMethod).getThrownTypes()) { |
|
1046 markThrown(resource, t); |
|
1047 } |
|
1048 } |
|
1049 } |
|
1050 } |
|
1051 } |
|
1052 scanStat(tree.body); |
|
1053 List<Type> thrownInTry = allowImprovedCatchAnalysis ? |
|
1054 chk.union(thrown, List.of(syms.runtimeExceptionType, syms.errorType)) : |
|
1055 thrown; |
|
1056 thrown = thrownPrev; |
|
1057 caught = caughtPrev; |
|
1058 boolean aliveEnd = alive; |
|
1059 uninitsTry.andSet(uninits); |
|
1060 Bits initsEnd = inits; |
|
1061 Bits uninitsEnd = uninits; |
|
1062 int nextadrCatch = nextadr; |
|
1063 |
|
1064 if (!resourceVarDecls.isEmpty() && |
|
1065 lint.isEnabled(Lint.LintCategory.TRY)) { |
|
1066 for (JCVariableDecl resVar : resourceVarDecls) { |
|
1067 if (unrefdResources.includes(resVar.sym)) { |
|
1068 log.warning(Lint.LintCategory.TRY, resVar.pos(), |
|
1069 "try.resource.not.referenced", resVar.sym); |
|
1070 unrefdResources.remove(resVar.sym); |
|
1071 } |
|
1072 } |
|
1073 } |
|
1074 |
|
1075 List<Type> caughtInTry = List.nil(); |
|
1076 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { |
|
1077 alive = true; |
|
1078 JCVariableDecl param = l.head.param; |
|
1079 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ? |
|
1080 ((JCTypeUnion)l.head.param.vartype).alternatives : |
|
1081 List.of(l.head.param.vartype); |
|
1082 List<Type> ctypes = List.nil(); |
|
1083 List<Type> rethrownTypes = chk.diff(thrownInTry, caughtInTry); |
|
1084 for (JCExpression ct : subClauses) { |
|
1085 Type exc = ct.type; |
|
1086 if (exc != syms.unknownType) { |
|
1087 ctypes = ctypes.append(exc); |
|
1088 if (types.isSameType(exc, syms.objectType)) |
|
1089 continue; |
|
1090 checkCaughtType(l.head.pos(), exc, thrownInTry, caughtInTry); |
|
1091 caughtInTry = chk.incl(exc, caughtInTry); |
|
1092 } |
|
1093 } |
|
1094 inits = initsTry.dup(); |
|
1095 uninits = uninitsTry.dup(); |
|
1096 scan(param); |
|
1097 inits.incl(param.sym.adr); |
|
1098 uninits.excl(param.sym.adr); |
|
1099 preciseRethrowTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes)); |
|
1100 scanStat(l.head.body); |
|
1101 initsEnd.andSet(inits); |
|
1102 uninitsEnd.andSet(uninits); |
|
1103 nextadr = nextadrCatch; |
|
1104 preciseRethrowTypes.remove(param.sym); |
|
1105 aliveEnd |= alive; |
|
1106 } |
|
1107 if (tree.finalizer != null) { |
|
1108 List<Type> savedThrown = thrown; |
|
1109 thrown = List.nil(); |
|
1110 inits = initsTry.dup(); |
|
1111 uninits = uninitsTry.dup(); |
|
1112 ListBuffer<PendingExit> exits = pendingExits; |
|
1113 pendingExits = prevPendingExits; |
|
1114 alive = true; |
|
1115 scanStat(tree.finalizer); |
|
1116 if (!alive) { |
|
1117 // discard exits and exceptions from try and finally |
|
1118 thrown = chk.union(thrown, thrownPrev); |
|
1119 if (!loopPassTwo && |
|
1120 lint.isEnabled(Lint.LintCategory.FINALLY)) { |
|
1121 log.warning(Lint.LintCategory.FINALLY, |
|
1122 TreeInfo.diagEndPos(tree.finalizer), |
|
1123 "finally.cannot.complete"); |
|
1124 } |
|
1125 } else { |
|
1126 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); |
|
1127 thrown = chk.union(thrown, savedThrown); |
|
1128 uninits.andSet(uninitsEnd); |
|
1129 // FIX: this doesn't preserve source order of exits in catch |
|
1130 // versus finally! |
|
1131 while (exits.nonEmpty()) { |
|
1132 PendingExit exit = exits.next(); |
|
1133 if (exit.inits != null) { |
|
1134 exit.inits.orSet(inits); |
|
1135 exit.uninits.andSet(uninits); |
|
1136 } |
|
1137 pendingExits.append(exit); |
|
1138 } |
|
1139 inits.orSet(initsEnd); |
|
1140 alive = aliveEnd; |
|
1141 } |
|
1142 } else { |
|
1143 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); |
|
1144 inits = initsEnd; |
|
1145 uninits = uninitsEnd; |
|
1146 alive = aliveEnd; |
|
1147 ListBuffer<PendingExit> exits = pendingExits; |
|
1148 pendingExits = prevPendingExits; |
|
1149 while (exits.nonEmpty()) pendingExits.append(exits.next()); |
|
1150 } |
|
1151 uninitsTry.andSet(uninitsTryPrev).andSet(uninits); |
|
1152 } |
|
1153 |
|
1154 void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) { |
|
1155 if (chk.subset(exc, caughtInTry)) { |
|
1156 log.error(pos, "except.already.caught", exc); |
|
1157 } else if (!chk.isUnchecked(pos, exc) && |
|
1158 !isExceptionOrThrowable(exc) && |
|
1159 !chk.intersects(exc, thrownInTry)) { |
|
1160 log.error(pos, "except.never.thrown.in.try", exc); |
|
1161 } else if (allowImprovedCatchAnalysis) { |
|
1162 List<Type> catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry); |
|
1163 // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an |
|
1164 // unchecked exception, the result list would not be empty, as the augmented |
|
1165 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked |
|
1166 // exception, that would have been covered in the branch above |
|
1167 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() && |
|
1168 !isExceptionOrThrowable(exc)) { |
|
1169 String key = catchableThrownTypes.length() == 1 ? |
|
1170 "unreachable.catch" : |
|
1171 "unreachable.catch.1"; |
|
1172 log.warning(pos, key, catchableThrownTypes); |
|
1173 } |
|
1174 } |
|
1175 } |
|
1176 //where |
|
1177 private boolean isExceptionOrThrowable(Type exc) { |
|
1178 return exc.tsym == syms.throwableType.tsym || |
|
1179 exc.tsym == syms.exceptionType.tsym; |
|
1180 } |
|
1181 |
|
1182 |
|
1183 public void visitConditional(JCConditional tree) { |
|
1184 scanCond(tree.cond); |
|
1185 Bits initsBeforeElse = initsWhenFalse; |
|
1186 Bits uninitsBeforeElse = uninitsWhenFalse; |
|
1187 inits = initsWhenTrue; |
|
1188 uninits = uninitsWhenTrue; |
|
1189 if (tree.truepart.type.tag == BOOLEAN && |
|
1190 tree.falsepart.type.tag == BOOLEAN) { |
|
1191 // if b and c are boolean valued, then |
|
1192 // v is (un)assigned after a?b:c when true iff |
|
1193 // v is (un)assigned after b when true and |
|
1194 // v is (un)assigned after c when true |
|
1195 scanCond(tree.truepart); |
|
1196 Bits initsAfterThenWhenTrue = initsWhenTrue.dup(); |
|
1197 Bits initsAfterThenWhenFalse = initsWhenFalse.dup(); |
|
1198 Bits uninitsAfterThenWhenTrue = uninitsWhenTrue.dup(); |
|
1199 Bits uninitsAfterThenWhenFalse = uninitsWhenFalse.dup(); |
|
1200 inits = initsBeforeElse; |
|
1201 uninits = uninitsBeforeElse; |
|
1202 scanCond(tree.falsepart); |
|
1203 initsWhenTrue.andSet(initsAfterThenWhenTrue); |
|
1204 initsWhenFalse.andSet(initsAfterThenWhenFalse); |
|
1205 uninitsWhenTrue.andSet(uninitsAfterThenWhenTrue); |
|
1206 uninitsWhenFalse.andSet(uninitsAfterThenWhenFalse); |
|
1207 } else { |
|
1208 scanExpr(tree.truepart); |
|
1209 Bits initsAfterThen = inits.dup(); |
|
1210 Bits uninitsAfterThen = uninits.dup(); |
|
1211 inits = initsBeforeElse; |
|
1212 uninits = uninitsBeforeElse; |
|
1213 scanExpr(tree.falsepart); |
|
1214 inits.andSet(initsAfterThen); |
|
1215 uninits.andSet(uninitsAfterThen); |
|
1216 } |
|
1217 } |
|
1218 |
|
1219 public void visitIf(JCIf tree) { |
|
1220 scanCond(tree.cond); |
|
1221 Bits initsBeforeElse = initsWhenFalse; |
|
1222 Bits uninitsBeforeElse = uninitsWhenFalse; |
|
1223 inits = initsWhenTrue; |
|
1224 uninits = uninitsWhenTrue; |
|
1225 scanStat(tree.thenpart); |
|
1226 if (tree.elsepart != null) { |
|
1227 boolean aliveAfterThen = alive; |
|
1228 alive = true; |
|
1229 Bits initsAfterThen = inits.dup(); |
|
1230 Bits uninitsAfterThen = uninits.dup(); |
|
1231 inits = initsBeforeElse; |
|
1232 uninits = uninitsBeforeElse; |
|
1233 scanStat(tree.elsepart); |
|
1234 inits.andSet(initsAfterThen); |
|
1235 uninits.andSet(uninitsAfterThen); |
|
1236 alive = alive | aliveAfterThen; |
|
1237 } else { |
|
1238 inits.andSet(initsBeforeElse); |
|
1239 uninits.andSet(uninitsBeforeElse); |
|
1240 alive = true; |
|
1241 } |
|
1242 } |
|
1243 |
|
1244 |
|
1245 |
|
1246 public void visitBreak(JCBreak tree) { |
|
1247 recordExit(tree); |
|
1248 } |
|
1249 |
|
1250 public void visitContinue(JCContinue tree) { |
|
1251 recordExit(tree); |
|
1252 } |
|
1253 |
|
1254 public void visitReturn(JCReturn tree) { |
|
1255 scanExpr(tree.expr); |
|
1256 // if not initial constructor, should markDead instead of recordExit |
|
1257 recordExit(tree); |
|
1258 } |
|
1259 |
|
1260 public void visitThrow(JCThrow tree) { |
|
1261 scanExpr(tree.expr); |
|
1262 Symbol sym = TreeInfo.symbol(tree.expr); |
|
1263 if (sym != null && |
|
1264 sym.kind == VAR && |
|
1265 (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 && |
|
1266 preciseRethrowTypes.get(sym) != null && |
|
1267 allowImprovedRethrowAnalysis) { |
|
1268 for (Type t : preciseRethrowTypes.get(sym)) { |
|
1269 markThrown(tree, t); |
|
1270 } |
|
1271 } |
|
1272 else { |
|
1273 markThrown(tree, tree.expr.type); |
|
1274 } |
|
1275 markDead(); |
|
1276 } |
|
1277 |
|
1278 public void visitApply(JCMethodInvocation tree) { |
|
1279 scanExpr(tree.meth); |
|
1280 scanExprs(tree.args); |
|
1281 for (List<Type> l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail) |
|
1282 markThrown(tree, l.head); |
|
1283 } |
|
1284 |
|
1285 public void visitNewClass(JCNewClass tree) { |
|
1286 scanExpr(tree.encl); |
|
1287 scanExprs(tree.args); |
|
1288 // scan(tree.def); |
|
1289 for (List<Type> l = tree.constructorType.getThrownTypes(); |
|
1290 l.nonEmpty(); |
|
1291 l = l.tail) { |
|
1292 markThrown(tree, l.head); |
|
1293 } |
|
1294 List<Type> caughtPrev = caught; |
|
1295 try { |
|
1296 // If the new class expression defines an anonymous class, |
|
1297 // analysis of the anonymous constructor may encounter thrown |
|
1298 // types which are unsubstituted type variables. |
|
1299 // However, since the constructor's actual thrown types have |
|
1300 // already been marked as thrown, it is safe to simply include |
|
1301 // each of the constructor's formal thrown types in the set of |
|
1302 // 'caught/declared to be thrown' types, for the duration of |
|
1303 // the class def analysis. |
|
1304 if (tree.def != null) |
|
1305 for (List<Type> l = tree.constructor.type.getThrownTypes(); |
|
1306 l.nonEmpty(); |
|
1307 l = l.tail) { |
|
1308 caught = chk.incl(l.head, caught); |
|
1309 } |
|
1310 scan(tree.def); |
|
1311 } |
|
1312 finally { |
|
1313 caught = caughtPrev; |
|
1314 } |
|
1315 } |
|
1316 |
|
1317 public void visitNewArray(JCNewArray tree) { |
|
1318 scanExprs(tree.dims); |
|
1319 scanExprs(tree.elems); |
|
1320 } |
|
1321 |
|
1322 public void visitAssert(JCAssert tree) { |
|
1323 Bits initsExit = inits.dup(); |
|
1324 Bits uninitsExit = uninits.dup(); |
|
1325 scanCond(tree.cond); |
|
1326 uninitsExit.andSet(uninitsWhenTrue); |
|
1327 if (tree.detail != null) { |
|
1328 inits = initsWhenFalse; |
|
1329 uninits = uninitsWhenFalse; |
|
1330 scanExpr(tree.detail); |
|
1331 } |
|
1332 inits = initsExit; |
|
1333 uninits = uninitsExit; |
|
1334 } |
|
1335 |
|
1336 public void visitAssign(JCAssign tree) { |
|
1337 JCTree lhs = TreeInfo.skipParens(tree.lhs); |
|
1338 if (!(lhs instanceof JCIdent)) scanExpr(lhs); |
|
1339 scanExpr(tree.rhs); |
|
1340 letInit(lhs); |
|
1341 } |
|
1342 |
|
1343 public void visitAssignop(JCAssignOp tree) { |
|
1344 scanExpr(tree.lhs); |
|
1345 scanExpr(tree.rhs); |
|
1346 letInit(tree.lhs); |
|
1347 } |
|
1348 |
|
1349 public void visitUnary(JCUnary tree) { |
|
1350 switch (tree.getTag()) { |
|
1351 case NOT: |
|
1352 scanCond(tree.arg); |
|
1353 Bits t = initsWhenFalse; |
|
1354 initsWhenFalse = initsWhenTrue; |
|
1355 initsWhenTrue = t; |
|
1356 t = uninitsWhenFalse; |
|
1357 uninitsWhenFalse = uninitsWhenTrue; |
|
1358 uninitsWhenTrue = t; |
|
1359 break; |
|
1360 case PREINC: case POSTINC: |
|
1361 case PREDEC: case POSTDEC: |
|
1362 scanExpr(tree.arg); |
|
1363 letInit(tree.arg); |
|
1364 break; |
|
1365 default: |
|
1366 scanExpr(tree.arg); |
|
1367 } |
|
1368 } |
|
1369 |
|
1370 public void visitBinary(JCBinary tree) { |
|
1371 switch (tree.getTag()) { |
|
1372 case AND: |
|
1373 scanCond(tree.lhs); |
|
1374 Bits initsWhenFalseLeft = initsWhenFalse; |
|
1375 Bits uninitsWhenFalseLeft = uninitsWhenFalse; |
|
1376 inits = initsWhenTrue; |
1568 inits = initsWhenTrue; |
1377 uninits = uninitsWhenTrue; |
1569 uninits = uninitsWhenTrue; |
1378 scanCond(tree.rhs); |
1570 if (tree.truepart.type.tag == BOOLEAN && |
1379 initsWhenFalse.andSet(initsWhenFalseLeft); |
1571 tree.falsepart.type.tag == BOOLEAN) { |
1380 uninitsWhenFalse.andSet(uninitsWhenFalseLeft); |
1572 // if b and c are boolean valued, then |
1381 break; |
1573 // v is (un)assigned after a?b:c when true iff |
1382 case OR: |
1574 // v is (un)assigned after b when true and |
1383 scanCond(tree.lhs); |
1575 // v is (un)assigned after c when true |
1384 Bits initsWhenTrueLeft = initsWhenTrue; |
1576 scanCond(tree.truepart); |
1385 Bits uninitsWhenTrueLeft = uninitsWhenTrue; |
1577 Bits initsAfterThenWhenTrue = initsWhenTrue.dup(); |
1386 inits = initsWhenFalse; |
1578 Bits initsAfterThenWhenFalse = initsWhenFalse.dup(); |
1387 uninits = uninitsWhenFalse; |
1579 Bits uninitsAfterThenWhenTrue = uninitsWhenTrue.dup(); |
1388 scanCond(tree.rhs); |
1580 Bits uninitsAfterThenWhenFalse = uninitsWhenFalse.dup(); |
1389 initsWhenTrue.andSet(initsWhenTrueLeft); |
1581 inits = initsBeforeElse; |
1390 uninitsWhenTrue.andSet(uninitsWhenTrueLeft); |
1582 uninits = uninitsBeforeElse; |
1391 break; |
1583 scanCond(tree.falsepart); |
1392 default: |
1584 initsWhenTrue.andSet(initsAfterThenWhenTrue); |
|
1585 initsWhenFalse.andSet(initsAfterThenWhenFalse); |
|
1586 uninitsWhenTrue.andSet(uninitsAfterThenWhenTrue); |
|
1587 uninitsWhenFalse.andSet(uninitsAfterThenWhenFalse); |
|
1588 } else { |
|
1589 scanExpr(tree.truepart); |
|
1590 Bits initsAfterThen = inits.dup(); |
|
1591 Bits uninitsAfterThen = uninits.dup(); |
|
1592 inits = initsBeforeElse; |
|
1593 uninits = uninitsBeforeElse; |
|
1594 scanExpr(tree.falsepart); |
|
1595 inits.andSet(initsAfterThen); |
|
1596 uninits.andSet(uninitsAfterThen); |
|
1597 } |
|
1598 } |
|
1599 |
|
1600 public void visitIf(JCIf tree) { |
|
1601 scanCond(tree.cond); |
|
1602 Bits initsBeforeElse = initsWhenFalse; |
|
1603 Bits uninitsBeforeElse = uninitsWhenFalse; |
|
1604 inits = initsWhenTrue; |
|
1605 uninits = uninitsWhenTrue; |
|
1606 scan(tree.thenpart); |
|
1607 if (tree.elsepart != null) { |
|
1608 Bits initsAfterThen = inits.dup(); |
|
1609 Bits uninitsAfterThen = uninits.dup(); |
|
1610 inits = initsBeforeElse; |
|
1611 uninits = uninitsBeforeElse; |
|
1612 scan(tree.elsepart); |
|
1613 inits.andSet(initsAfterThen); |
|
1614 uninits.andSet(uninitsAfterThen); |
|
1615 } else { |
|
1616 inits.andSet(initsBeforeElse); |
|
1617 uninits.andSet(uninitsBeforeElse); |
|
1618 } |
|
1619 } |
|
1620 |
|
1621 public void visitBreak(JCBreak tree) { |
|
1622 recordExit(tree, new AssignPendingExit(tree, inits, uninits)); |
|
1623 } |
|
1624 |
|
1625 public void visitContinue(JCContinue tree) { |
|
1626 recordExit(tree, new AssignPendingExit(tree, inits, uninits)); |
|
1627 } |
|
1628 |
|
1629 public void visitReturn(JCReturn tree) { |
|
1630 scanExpr(tree.expr); |
|
1631 // if not initial constructor, should markDead instead of recordExit |
|
1632 recordExit(tree, new AssignPendingExit(tree, inits, uninits)); |
|
1633 } |
|
1634 |
|
1635 public void visitThrow(JCThrow tree) { |
|
1636 scanExpr(tree.expr); |
|
1637 markDead(); |
|
1638 } |
|
1639 |
|
1640 public void visitApply(JCMethodInvocation tree) { |
|
1641 scanExpr(tree.meth); |
|
1642 scanExprs(tree.args); |
|
1643 } |
|
1644 |
|
1645 public void visitNewClass(JCNewClass tree) { |
|
1646 scanExpr(tree.encl); |
|
1647 scanExprs(tree.args); |
|
1648 scan(tree.def); |
|
1649 } |
|
1650 |
|
1651 public void visitNewArray(JCNewArray tree) { |
|
1652 scanExprs(tree.dims); |
|
1653 scanExprs(tree.elems); |
|
1654 } |
|
1655 |
|
1656 public void visitAssert(JCAssert tree) { |
|
1657 Bits initsExit = inits.dup(); |
|
1658 Bits uninitsExit = uninits.dup(); |
|
1659 scanCond(tree.cond); |
|
1660 uninitsExit.andSet(uninitsWhenTrue); |
|
1661 if (tree.detail != null) { |
|
1662 inits = initsWhenFalse; |
|
1663 uninits = uninitsWhenFalse; |
|
1664 scanExpr(tree.detail); |
|
1665 } |
|
1666 inits = initsExit; |
|
1667 uninits = uninitsExit; |
|
1668 } |
|
1669 |
|
1670 public void visitAssign(JCAssign tree) { |
|
1671 JCTree lhs = TreeInfo.skipParens(tree.lhs); |
|
1672 if (!(lhs instanceof JCIdent)) scanExpr(lhs); |
|
1673 scanExpr(tree.rhs); |
|
1674 letInit(lhs); |
|
1675 } |
|
1676 |
|
1677 public void visitAssignop(JCAssignOp tree) { |
1393 scanExpr(tree.lhs); |
1678 scanExpr(tree.lhs); |
1394 scanExpr(tree.rhs); |
1679 scanExpr(tree.rhs); |
1395 } |
1680 letInit(tree.lhs); |
1396 } |
1681 } |
1397 |
1682 |
1398 public void visitIdent(JCIdent tree) { |
1683 public void visitUnary(JCUnary tree) { |
1399 if (tree.sym.kind == VAR) { |
1684 switch (tree.getTag()) { |
1400 checkInit(tree.pos(), (VarSymbol)tree.sym); |
1685 case NOT: |
1401 referenced(tree.sym); |
1686 scanCond(tree.arg); |
1402 } |
1687 Bits t = initsWhenFalse; |
1403 } |
1688 initsWhenFalse = initsWhenTrue; |
1404 |
1689 initsWhenTrue = t; |
1405 void referenced(Symbol sym) { |
1690 t = uninitsWhenFalse; |
1406 unrefdResources.remove(sym); |
1691 uninitsWhenFalse = uninitsWhenTrue; |
1407 } |
1692 uninitsWhenTrue = t; |
1408 |
1693 break; |
1409 public void visitTypeCast(JCTypeCast tree) { |
1694 case PREINC: case POSTINC: |
1410 super.visitTypeCast(tree); |
1695 case PREDEC: case POSTDEC: |
1411 if (!tree.type.isErroneous() |
1696 scanExpr(tree.arg); |
1412 && lint.isEnabled(Lint.LintCategory.CAST) |
1697 letInit(tree.arg); |
1413 && types.isSameType(tree.expr.type, tree.clazz.type) |
1698 break; |
1414 && !is292targetTypeCast(tree)) { |
1699 default: |
1415 log.warning(Lint.LintCategory.CAST, |
1700 scanExpr(tree.arg); |
1416 tree.pos(), "redundant.cast", tree.expr.type); |
1701 } |
1417 } |
1702 } |
1418 } |
1703 |
1419 //where |
1704 public void visitBinary(JCBinary tree) { |
1420 private boolean is292targetTypeCast(JCTypeCast tree) { |
1705 switch (tree.getTag()) { |
1421 boolean is292targetTypeCast = false; |
1706 case AND: |
1422 JCExpression expr = TreeInfo.skipParens(tree.expr); |
1707 scanCond(tree.lhs); |
1423 if (expr.hasTag(APPLY)) { |
1708 Bits initsWhenFalseLeft = initsWhenFalse; |
1424 JCMethodInvocation apply = (JCMethodInvocation)expr; |
1709 Bits uninitsWhenFalseLeft = uninitsWhenFalse; |
1425 Symbol sym = TreeInfo.symbol(apply.meth); |
1710 inits = initsWhenTrue; |
1426 is292targetTypeCast = sym != null && |
1711 uninits = uninitsWhenTrue; |
1427 sym.kind == MTH && |
1712 scanCond(tree.rhs); |
1428 (sym.flags() & POLYMORPHIC_SIGNATURE) != 0; |
1713 initsWhenFalse.andSet(initsWhenFalseLeft); |
1429 } |
1714 uninitsWhenFalse.andSet(uninitsWhenFalseLeft); |
1430 return is292targetTypeCast; |
1715 break; |
1431 } |
1716 case OR: |
1432 |
1717 scanCond(tree.lhs); |
1433 public void visitTopLevel(JCCompilationUnit tree) { |
1718 Bits initsWhenTrueLeft = initsWhenTrue; |
1434 // Do nothing for TopLevel since each class is visited individually |
1719 Bits uninitsWhenTrueLeft = uninitsWhenTrue; |
1435 } |
1720 inits = initsWhenFalse; |
1436 |
1721 uninits = uninitsWhenFalse; |
1437 /************************************************************************** |
1722 scanCond(tree.rhs); |
1438 * main method |
1723 initsWhenTrue.andSet(initsWhenTrueLeft); |
1439 *************************************************************************/ |
1724 uninitsWhenTrue.andSet(uninitsWhenTrueLeft); |
1440 |
1725 break; |
1441 /** Perform definite assignment/unassignment analysis on a tree. |
1726 default: |
1442 */ |
1727 scanExpr(tree.lhs); |
1443 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { |
1728 scanExpr(tree.rhs); |
1444 try { |
1729 } |
1445 attrEnv = env; |
1730 } |
1446 JCTree tree = env.tree; |
1731 |
1447 this.make = make; |
1732 public void visitIdent(JCIdent tree) { |
1448 inits = new Bits(); |
1733 if (tree.sym.kind == VAR) { |
1449 uninits = new Bits(); |
1734 checkInit(tree.pos(), (VarSymbol)tree.sym); |
1450 uninitsTry = new Bits(); |
1735 referenced(tree.sym); |
1451 initsWhenTrue = initsWhenFalse = |
1736 } |
1452 uninitsWhenTrue = uninitsWhenFalse = null; |
1737 } |
1453 if (vars == null) |
1738 |
1454 vars = new VarSymbol[32]; |
1739 void referenced(Symbol sym) { |
1455 else |
1740 unrefdResources.remove(sym); |
1456 for (int i=0; i<vars.length; i++) |
1741 } |
|
1742 |
|
1743 public void visitTopLevel(JCCompilationUnit tree) { |
|
1744 // Do nothing for TopLevel since each class is visited individually |
|
1745 } |
|
1746 |
|
1747 /************************************************************************** |
|
1748 * main method |
|
1749 *************************************************************************/ |
|
1750 |
|
1751 /** Perform definite assignment/unassignment analysis on a tree. |
|
1752 */ |
|
1753 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { |
|
1754 try { |
|
1755 attrEnv = env; |
|
1756 JCTree tree = env.tree; |
|
1757 Flow.this.make = make; |
|
1758 inits = new Bits(); |
|
1759 uninits = new Bits(); |
|
1760 uninitsTry = new Bits(); |
|
1761 initsWhenTrue = initsWhenFalse = |
|
1762 uninitsWhenTrue = uninitsWhenFalse = null; |
|
1763 if (vars == null) |
|
1764 vars = new VarSymbol[32]; |
|
1765 else |
|
1766 for (int i=0; i<vars.length; i++) |
|
1767 vars[i] = null; |
|
1768 firstadr = 0; |
|
1769 nextadr = 0; |
|
1770 pendingExits = new ListBuffer<AssignPendingExit>(); |
|
1771 this.classDef = null; |
|
1772 unrefdResources = new Scope(env.enclClass.sym); |
|
1773 scan(tree); |
|
1774 } finally { |
|
1775 // note that recursive invocations of this method fail hard |
|
1776 inits = uninits = uninitsTry = null; |
|
1777 initsWhenTrue = initsWhenFalse = |
|
1778 uninitsWhenTrue = uninitsWhenFalse = null; |
|
1779 if (vars != null) for (int i=0; i<vars.length; i++) |
1457 vars[i] = null; |
1780 vars[i] = null; |
1458 firstadr = 0; |
1781 firstadr = 0; |
1459 nextadr = 0; |
1782 nextadr = 0; |
1460 pendingExits = new ListBuffer<PendingExit>(); |
1783 pendingExits = null; |
1461 preciseRethrowTypes = new HashMap<Symbol, List<Type>>(); |
1784 Flow.this.make = null; |
1462 alive = true; |
1785 this.classDef = null; |
1463 this.thrown = this.caught = null; |
1786 unrefdResources = null; |
1464 this.classDef = null; |
1787 } |
1465 unrefdResources = new Scope(env.enclClass.sym); |
|
1466 scan(tree); |
|
1467 } finally { |
|
1468 // note that recursive invocations of this method fail hard |
|
1469 inits = uninits = uninitsTry = null; |
|
1470 initsWhenTrue = initsWhenFalse = |
|
1471 uninitsWhenTrue = uninitsWhenFalse = null; |
|
1472 if (vars != null) for (int i=0; i<vars.length; i++) |
|
1473 vars[i] = null; |
|
1474 firstadr = 0; |
|
1475 nextadr = 0; |
|
1476 pendingExits = null; |
|
1477 this.make = null; |
|
1478 this.thrown = this.caught = null; |
|
1479 this.classDef = null; |
|
1480 unrefdResources = null; |
|
1481 } |
1788 } |
1482 } |
1789 } |
1483 } |
1790 } |