298 |
298 |
299 /** |
299 /** |
300 * Base visitor class for all visitors implementing dataflow analysis logic. |
300 * Base visitor class for all visitors implementing dataflow analysis logic. |
301 * This class define the shared logic for handling jumps (break/continue statements). |
301 * This class define the shared logic for handling jumps (break/continue statements). |
302 */ |
302 */ |
303 static abstract class BaseAnalyzer<P extends BaseAnalyzer.PendingExit> extends TreeScanner { |
303 static abstract class BaseAnalyzer extends TreeScanner { |
304 |
304 |
305 enum JumpKind { |
305 enum JumpKind { |
306 BREAK(JCTree.Tag.BREAK) { |
306 BREAK(JCTree.Tag.BREAK) { |
307 @Override |
307 @Override |
308 JCTree getTarget(JCTree tree) { |
308 JCTree getTarget(JCTree tree) { |
326 } |
326 } |
327 |
327 |
328 /** The currently pending exits that go from current inner blocks |
328 /** The currently pending exits that go from current inner blocks |
329 * to an enclosing block, in source order. |
329 * to an enclosing block, in source order. |
330 */ |
330 */ |
331 ListBuffer<P> pendingExits; |
331 ListBuffer<PendingExit> pendingExits; |
332 |
332 |
333 /** A pending exit. These are the statements return, break, and |
333 /** A pending exit. These are the statements return, break, and |
334 * continue. In addition, exception-throwing expressions or |
334 * continue. In addition, exception-throwing expressions or |
335 * statements are put here when not known to be caught. This |
335 * statements are put here when not known to be caught. This |
336 * will typically result in an error unless it is within a |
336 * will typically result in an error unless it is within a |
349 } |
349 } |
350 |
350 |
351 abstract void markDead(); |
351 abstract void markDead(); |
352 |
352 |
353 /** Record an outward transfer of control. */ |
353 /** Record an outward transfer of control. */ |
354 void recordExit(P pe) { |
354 void recordExit(PendingExit pe) { |
355 pendingExits.append(pe); |
355 pendingExits.append(pe); |
356 markDead(); |
356 markDead(); |
357 } |
357 } |
358 |
358 |
359 /** Resolve all jumps of this statement. */ |
359 /** Resolve all jumps of this statement. */ |
360 private Liveness resolveJump(JCTree tree, |
360 private Liveness resolveJump(JCTree tree, |
361 ListBuffer<P> oldPendingExits, |
361 ListBuffer<PendingExit> oldPendingExits, |
362 JumpKind jk) { |
362 JumpKind jk) { |
363 boolean resolved = false; |
363 boolean resolved = false; |
364 List<P> exits = pendingExits.toList(); |
364 List<PendingExit> exits = pendingExits.toList(); |
365 pendingExits = oldPendingExits; |
365 pendingExits = oldPendingExits; |
366 for (; exits.nonEmpty(); exits = exits.tail) { |
366 for (; exits.nonEmpty(); exits = exits.tail) { |
367 P exit = exits.head; |
367 PendingExit exit = exits.head; |
368 if (exit.tree.hasTag(jk.treeTag) && |
368 if (exit.tree.hasTag(jk.treeTag) && |
369 jk.getTarget(exit.tree) == tree) { |
369 jk.getTarget(exit.tree) == tree) { |
370 exit.resolveJump(); |
370 exit.resolveJump(); |
371 resolved = true; |
371 resolved = true; |
372 } else { |
372 } else { |
376 return Liveness.from(resolved); |
376 return Liveness.from(resolved); |
377 } |
377 } |
378 |
378 |
379 /** Resolve all continues of this statement. */ |
379 /** Resolve all continues of this statement. */ |
380 Liveness resolveContinues(JCTree tree) { |
380 Liveness resolveContinues(JCTree tree) { |
381 return resolveJump(tree, new ListBuffer<P>(), JumpKind.CONTINUE); |
381 return resolveJump(tree, new ListBuffer<PendingExit>(), JumpKind.CONTINUE); |
382 } |
382 } |
383 |
383 |
384 /** Resolve all breaks of this statement. */ |
384 /** Resolve all breaks of this statement. */ |
385 Liveness resolveBreaks(JCTree tree, ListBuffer<P> oldPendingExits) { |
385 Liveness resolveBreaks(JCTree tree, ListBuffer<PendingExit> oldPendingExits) { |
386 return resolveJump(tree, oldPendingExits, JumpKind.BREAK); |
386 return resolveJump(tree, oldPendingExits, JumpKind.BREAK); |
387 } |
387 } |
388 |
388 |
389 @Override |
389 @Override |
390 public void scan(JCTree tree) { |
390 public void scan(JCTree tree) { |
410 * This pass implements the first step of the dataflow analysis, namely |
410 * This pass implements the first step of the dataflow analysis, namely |
411 * the liveness analysis check. This checks that every statement is reachable. |
411 * the liveness analysis check. This checks that every statement is reachable. |
412 * The output of this analysis pass are used by other analyzers. This analyzer |
412 * The output of this analysis pass are used by other analyzers. This analyzer |
413 * sets the 'finallyCanCompleteNormally' field in the JCTry class. |
413 * sets the 'finallyCanCompleteNormally' field in the JCTry class. |
414 */ |
414 */ |
415 class AliveAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> { |
415 class AliveAnalyzer extends BaseAnalyzer { |
416 |
416 |
417 /** A flag that indicates whether the last statement could |
417 /** A flag that indicates whether the last statement could |
418 * complete normally. |
418 * complete normally. |
419 */ |
419 */ |
420 private Liveness alive; |
420 private Liveness alive; |
829 * This pass implements the second step of the dataflow analysis, namely |
829 * This pass implements the second step of the dataflow analysis, namely |
830 * the exception analysis. This is to ensure that every checked exception that is |
830 * the exception analysis. This is to ensure that every checked exception that is |
831 * thrown is declared or caught. The analyzer uses some info that has been set by |
831 * thrown is declared or caught. The analyzer uses some info that has been set by |
832 * the liveliness analyzer. |
832 * the liveliness analyzer. |
833 */ |
833 */ |
834 class FlowAnalyzer extends BaseAnalyzer<FlowAnalyzer.FlowPendingExit> { |
834 class FlowAnalyzer extends BaseAnalyzer { |
835 |
835 |
836 /** A flag that indicates whether the last statement could |
836 /** A flag that indicates whether the last statement could |
837 * complete normally. |
837 * complete normally. |
838 */ |
838 */ |
839 HashMap<Symbol, List<Type>> preciseRethrowTypes; |
839 HashMap<Symbol, List<Type>> preciseRethrowTypes; |
869 /*-------------------- Exceptions ----------------------*/ |
869 /*-------------------- Exceptions ----------------------*/ |
870 |
870 |
871 /** Complain that pending exceptions are not caught. |
871 /** Complain that pending exceptions are not caught. |
872 */ |
872 */ |
873 void errorUncaught() { |
873 void errorUncaught() { |
874 for (FlowPendingExit exit = pendingExits.next(); |
874 for (PendingExit exit = pendingExits.next(); |
875 exit != null; |
875 exit != null; |
876 exit = pendingExits.next()) { |
876 exit = pendingExits.next()) { |
|
877 Assert.check(exit instanceof ThrownPendingExit); |
|
878 ThrownPendingExit thrownExit = (ThrownPendingExit) exit; |
877 if (classDef != null && |
879 if (classDef != null && |
878 classDef.pos == exit.tree.pos) { |
880 classDef.pos == exit.tree.pos) { |
879 log.error(exit.tree.pos(), |
881 log.error(exit.tree.pos(), |
880 Errors.UnreportedExceptionDefaultConstructor(exit.thrown)); |
882 Errors.UnreportedExceptionDefaultConstructor(thrownExit.thrown)); |
881 } else if (exit.tree.hasTag(VARDEF) && |
883 } else if (exit.tree.hasTag(VARDEF) && |
882 ((JCVariableDecl)exit.tree).sym.isResourceVariable()) { |
884 ((JCVariableDecl)exit.tree).sym.isResourceVariable()) { |
883 log.error(exit.tree.pos(), |
885 log.error(exit.tree.pos(), |
884 Errors.UnreportedExceptionImplicitClose(exit.thrown, |
886 Errors.UnreportedExceptionImplicitClose(thrownExit.thrown, |
885 ((JCVariableDecl)exit.tree).sym.name)); |
887 ((JCVariableDecl)exit.tree).sym.name)); |
886 } else { |
888 } else { |
887 log.error(exit.tree.pos(), |
889 log.error(exit.tree.pos(), |
888 Errors.UnreportedExceptionNeedToCatchOrThrow(exit.thrown)); |
890 Errors.UnreportedExceptionNeedToCatchOrThrow(thrownExit.thrown)); |
889 } |
891 } |
890 } |
892 } |
891 } |
893 } |
892 |
894 |
893 /** Record that exception is potentially thrown and check that it |
895 /** Record that exception is potentially thrown and check that it |
894 * is caught. |
896 * is caught. |
895 */ |
897 */ |
896 void markThrown(JCTree tree, Type exc) { |
898 void markThrown(JCTree tree, Type exc) { |
897 if (!chk.isUnchecked(tree.pos(), exc)) { |
899 if (!chk.isUnchecked(tree.pos(), exc)) { |
898 if (!chk.isHandled(exc, caught)) { |
900 if (!chk.isHandled(exc, caught)) { |
899 pendingExits.append(new FlowPendingExit(tree, exc)); |
901 pendingExits.append(new ThrownPendingExit(tree, exc)); |
900 } |
902 } |
901 thrown = chk.incl(exc, thrown); |
903 thrown = chk.incl(exc, thrown); |
902 } |
904 } |
903 } |
905 } |
904 |
906 |
912 if (tree.sym == null) return; |
914 if (tree.sym == null) return; |
913 |
915 |
914 JCClassDecl classDefPrev = classDef; |
916 JCClassDecl classDefPrev = classDef; |
915 List<Type> thrownPrev = thrown; |
917 List<Type> thrownPrev = thrown; |
916 List<Type> caughtPrev = caught; |
918 List<Type> caughtPrev = caught; |
917 ListBuffer<FlowPendingExit> pendingExitsPrev = pendingExits; |
919 ListBuffer<PendingExit> pendingExitsPrev = pendingExits; |
918 Lint lintPrev = lint; |
920 Lint lintPrev = lint; |
919 boolean anonymousClass = tree.name == names.empty; |
921 boolean anonymousClass = tree.name == names.empty; |
920 pendingExits = new ListBuffer<>(); |
922 pendingExits = new ListBuffer<>(); |
921 if (!anonymousClass) { |
923 if (!anonymousClass) { |
922 caught = List.nil(); |
924 caught = List.nil(); |
1022 // else we are in an instance initializer block; |
1024 // else we are in an instance initializer block; |
1023 // leave caught unchanged. |
1025 // leave caught unchanged. |
1024 |
1026 |
1025 scan(tree.body); |
1027 scan(tree.body); |
1026 |
1028 |
1027 List<FlowPendingExit> exits = pendingExits.toList(); |
1029 List<PendingExit> exits = pendingExits.toList(); |
1028 pendingExits = new ListBuffer<>(); |
1030 pendingExits = new ListBuffer<>(); |
1029 while (exits.nonEmpty()) { |
1031 while (exits.nonEmpty()) { |
1030 FlowPendingExit exit = exits.head; |
1032 PendingExit exit = exits.head; |
1031 exits = exits.tail; |
1033 exits = exits.tail; |
1032 if (exit.thrown == null) { |
1034 if (!(exit instanceof ThrownPendingExit)) { |
1033 Assert.check(exit.tree.hasTag(RETURN)); |
1035 Assert.check(exit.tree.hasTag(RETURN)); |
1034 } else { |
1036 } else { |
1035 // uncaught throws will be reported later |
1037 // uncaught throws will be reported later |
1036 pendingExits.append(exit); |
1038 pendingExits.append(exit); |
1037 } |
1039 } |
1057 public void visitBlock(JCBlock tree) { |
1059 public void visitBlock(JCBlock tree) { |
1058 scan(tree.stats); |
1060 scan(tree.stats); |
1059 } |
1061 } |
1060 |
1062 |
1061 public void visitDoLoop(JCDoWhileLoop tree) { |
1063 public void visitDoLoop(JCDoWhileLoop tree) { |
1062 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
1064 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
1063 pendingExits = new ListBuffer<>(); |
1065 pendingExits = new ListBuffer<>(); |
1064 scan(tree.body); |
1066 scan(tree.body); |
1065 resolveContinues(tree); |
1067 resolveContinues(tree); |
1066 scan(tree.cond); |
1068 scan(tree.cond); |
1067 resolveBreaks(tree, prevPendingExits); |
1069 resolveBreaks(tree, prevPendingExits); |
1068 } |
1070 } |
1069 |
1071 |
1070 public void visitWhileLoop(JCWhileLoop tree) { |
1072 public void visitWhileLoop(JCWhileLoop tree) { |
1071 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
1073 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
1072 pendingExits = new ListBuffer<>(); |
1074 pendingExits = new ListBuffer<>(); |
1073 scan(tree.cond); |
1075 scan(tree.cond); |
1074 scan(tree.body); |
1076 scan(tree.body); |
1075 resolveContinues(tree); |
1077 resolveContinues(tree); |
1076 resolveBreaks(tree, prevPendingExits); |
1078 resolveBreaks(tree, prevPendingExits); |
1077 } |
1079 } |
1078 |
1080 |
1079 public void visitForLoop(JCForLoop tree) { |
1081 public void visitForLoop(JCForLoop tree) { |
1080 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
1082 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
1081 scan(tree.init); |
1083 scan(tree.init); |
1082 pendingExits = new ListBuffer<>(); |
1084 pendingExits = new ListBuffer<>(); |
1083 if (tree.cond != null) { |
1085 if (tree.cond != null) { |
1084 scan(tree.cond); |
1086 scan(tree.cond); |
1085 } |
1087 } |
1089 resolveBreaks(tree, prevPendingExits); |
1091 resolveBreaks(tree, prevPendingExits); |
1090 } |
1092 } |
1091 |
1093 |
1092 public void visitForeachLoop(JCEnhancedForLoop tree) { |
1094 public void visitForeachLoop(JCEnhancedForLoop tree) { |
1093 visitVarDef(tree.var); |
1095 visitVarDef(tree.var); |
1094 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
1096 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
1095 scan(tree.expr); |
1097 scan(tree.expr); |
1096 pendingExits = new ListBuffer<>(); |
1098 pendingExits = new ListBuffer<>(); |
1097 scan(tree.body); |
1099 scan(tree.body); |
1098 resolveContinues(tree); |
1100 resolveContinues(tree); |
1099 resolveBreaks(tree, prevPendingExits); |
1101 resolveBreaks(tree, prevPendingExits); |
1100 } |
1102 } |
1101 |
1103 |
1102 public void visitLabelled(JCLabeledStatement tree) { |
1104 public void visitLabelled(JCLabeledStatement tree) { |
1103 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
1105 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
1104 pendingExits = new ListBuffer<>(); |
1106 pendingExits = new ListBuffer<>(); |
1105 scan(tree.body); |
1107 scan(tree.body); |
1106 resolveBreaks(tree, prevPendingExits); |
1108 resolveBreaks(tree, prevPendingExits); |
1107 } |
1109 } |
1108 |
1110 |
1114 public void visitSwitchExpression(JCSwitchExpression tree) { |
1116 public void visitSwitchExpression(JCSwitchExpression tree) { |
1115 handleSwitch(tree, tree.selector, tree.cases); |
1117 handleSwitch(tree, tree.selector, tree.cases); |
1116 } |
1118 } |
1117 |
1119 |
1118 private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) { |
1120 private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) { |
1119 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
1121 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
1120 pendingExits = new ListBuffer<>(); |
1122 pendingExits = new ListBuffer<>(); |
1121 scan(selector); |
1123 scan(selector); |
1122 for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) { |
1124 for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) { |
1123 JCCase c = l.head; |
1125 JCCase c = l.head; |
1124 scan(c.pats); |
1126 scan(c.pats); |
1138 for (JCExpression ct : subClauses) { |
1140 for (JCExpression ct : subClauses) { |
1139 caught = chk.incl(ct.type, caught); |
1141 caught = chk.incl(ct.type, caught); |
1140 } |
1142 } |
1141 } |
1143 } |
1142 |
1144 |
1143 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
1145 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
1144 pendingExits = new ListBuffer<>(); |
1146 pendingExits = new ListBuffer<>(); |
1145 for (JCTree resource : tree.resources) { |
1147 for (JCTree resource : tree.resources) { |
1146 if (resource instanceof JCVariableDecl) { |
1148 if (resource instanceof JCVariableDecl) { |
1147 JCVariableDecl vdecl = (JCVariableDecl) resource; |
1149 JCVariableDecl vdecl = (JCVariableDecl) resource; |
1148 visitVarDef(vdecl); |
1150 visitVarDef(vdecl); |
1202 preciseRethrowTypes.remove(param.sym); |
1204 preciseRethrowTypes.remove(param.sym); |
1203 } |
1205 } |
1204 if (tree.finalizer != null) { |
1206 if (tree.finalizer != null) { |
1205 List<Type> savedThrown = thrown; |
1207 List<Type> savedThrown = thrown; |
1206 thrown = List.nil(); |
1208 thrown = List.nil(); |
1207 ListBuffer<FlowPendingExit> exits = pendingExits; |
1209 ListBuffer<PendingExit> exits = pendingExits; |
1208 pendingExits = prevPendingExits; |
1210 pendingExits = prevPendingExits; |
1209 scan(tree.finalizer); |
1211 scan(tree.finalizer); |
1210 if (!tree.finallyCanCompleteNormally) { |
1212 if (!tree.finallyCanCompleteNormally) { |
1211 // discard exits and exceptions from try and finally |
1213 // discard exits and exceptions from try and finally |
1212 thrown = chk.union(thrown, thrownPrev); |
1214 thrown = chk.union(thrown, thrownPrev); |
1219 pendingExits.append(exits.next()); |
1221 pendingExits.append(exits.next()); |
1220 } |
1222 } |
1221 } |
1223 } |
1222 } else { |
1224 } else { |
1223 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); |
1225 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); |
1224 ListBuffer<FlowPendingExit> exits = pendingExits; |
1226 ListBuffer<PendingExit> exits = pendingExits; |
1225 pendingExits = prevPendingExits; |
1227 pendingExits = prevPendingExits; |
1226 while (exits.nonEmpty()) pendingExits.append(exits.next()); |
1228 while (exits.nonEmpty()) pendingExits.append(exits.next()); |
1227 } |
1229 } |
1228 } |
1230 } |
1229 |
1231 |
1265 } |
1267 } |
1266 |
1268 |
1267 public void visitBreak(JCBreak tree) { |
1269 public void visitBreak(JCBreak tree) { |
1268 if (tree.isValueBreak()) |
1270 if (tree.isValueBreak()) |
1269 scan(tree.value); |
1271 scan(tree.value); |
1270 recordExit(new FlowPendingExit(tree, null)); |
1272 recordExit(new PendingExit(tree)); |
1271 } |
1273 } |
1272 |
1274 |
1273 public void visitContinue(JCContinue tree) { |
1275 public void visitContinue(JCContinue tree) { |
1274 recordExit(new FlowPendingExit(tree, null)); |
1276 recordExit(new PendingExit(tree)); |
1275 } |
1277 } |
1276 |
1278 |
1277 public void visitReturn(JCReturn tree) { |
1279 public void visitReturn(JCReturn tree) { |
1278 scan(tree.expr); |
1280 scan(tree.expr); |
1279 recordExit(new FlowPendingExit(tree, null)); |
1281 recordExit(new PendingExit(tree)); |
1280 } |
1282 } |
1281 |
1283 |
1282 public void visitThrow(JCThrow tree) { |
1284 public void visitThrow(JCThrow tree) { |
1283 scan(tree.expr); |
1285 scan(tree.expr); |
1284 Symbol sym = TreeInfo.symbol(tree.expr); |
1286 Symbol sym = TreeInfo.symbol(tree.expr); |
1341 tree.type.isErroneous()) { |
1343 tree.type.isErroneous()) { |
1342 return; |
1344 return; |
1343 } |
1345 } |
1344 List<Type> prevCaught = caught; |
1346 List<Type> prevCaught = caught; |
1345 List<Type> prevThrown = thrown; |
1347 List<Type> prevThrown = thrown; |
1346 ListBuffer<FlowPendingExit> prevPending = pendingExits; |
1348 ListBuffer<PendingExit> prevPending = pendingExits; |
1347 try { |
1349 try { |
1348 pendingExits = new ListBuffer<>(); |
1350 pendingExits = new ListBuffer<>(); |
1349 caught = tree.getDescriptorType(types).getThrownTypes(); |
1351 caught = tree.getDescriptorType(types).getThrownTypes(); |
1350 thrown = List.nil(); |
1352 thrown = List.nil(); |
1351 scan(tree.body); |
1353 scan(tree.body); |
1352 List<FlowPendingExit> exits = pendingExits.toList(); |
1354 List<PendingExit> exits = pendingExits.toList(); |
1353 pendingExits = new ListBuffer<>(); |
1355 pendingExits = new ListBuffer<>(); |
1354 while (exits.nonEmpty()) { |
1356 while (exits.nonEmpty()) { |
1355 FlowPendingExit exit = exits.head; |
1357 PendingExit exit = exits.head; |
1356 exits = exits.tail; |
1358 exits = exits.tail; |
1357 if (exit.thrown == null) { |
1359 if (!(exit instanceof ThrownPendingExit)) { |
1358 Assert.check(exit.tree.hasTag(RETURN)); |
1360 Assert.check(exit.tree.hasTag(RETURN)); |
1359 } else { |
1361 } else { |
1360 // uncaught throws will be reported later |
1362 // uncaught throws will be reported later |
1361 pendingExits.append(exit); |
1363 pendingExits.append(exit); |
1362 } |
1364 } |
1486 tree.type.isErroneous()) || inLambda) { |
1488 tree.type.isErroneous()) || inLambda) { |
1487 return; |
1489 return; |
1488 } |
1490 } |
1489 List<Type> prevCaught = caught; |
1491 List<Type> prevCaught = caught; |
1490 List<Type> prevThrown = thrown; |
1492 List<Type> prevThrown = thrown; |
1491 ListBuffer<FlowPendingExit> prevPending = pendingExits; |
1493 ListBuffer<PendingExit> prevPending = pendingExits; |
1492 inLambda = true; |
1494 inLambda = true; |
1493 try { |
1495 try { |
1494 pendingExits = new ListBuffer<>(); |
1496 pendingExits = new ListBuffer<>(); |
1495 caught = List.of(syms.throwableType); |
1497 caught = List.of(syms.throwableType); |
1496 thrown = List.nil(); |
1498 thrown = List.nil(); |
1515 * which ensures that no final variable is assigned more than once. This visitor |
1517 * which ensures that no final variable is assigned more than once. This visitor |
1516 * depends on the results of the liveliness analyzer. This pass is also used to mark |
1518 * depends on the results of the liveliness analyzer. This pass is also used to mark |
1517 * effectively-final local variables/parameters. |
1519 * effectively-final local variables/parameters. |
1518 */ |
1520 */ |
1519 |
1521 |
1520 public class AssignAnalyzer extends BaseAnalyzer<AssignAnalyzer.AssignPendingExit> { |
1522 public class AssignAnalyzer extends BaseAnalyzer { |
1521 |
1523 |
1522 /** The set of definitely assigned variables. |
1524 /** The set of definitely assigned variables. |
1523 */ |
1525 */ |
1524 final Bits inits; |
1526 final Bits inits; |
1525 |
1527 |
1833 } |
1835 } |
1834 |
1836 |
1835 JCClassDecl classDefPrev = classDef; |
1837 JCClassDecl classDefPrev = classDef; |
1836 int firstadrPrev = firstadr; |
1838 int firstadrPrev = firstadr; |
1837 int nextadrPrev = nextadr; |
1839 int nextadrPrev = nextadr; |
1838 ListBuffer<AssignPendingExit> pendingExitsPrev = pendingExits; |
1840 ListBuffer<PendingExit> pendingExitsPrev = pendingExits; |
1839 |
1841 |
1840 pendingExits = new ListBuffer<>(); |
1842 pendingExits = new ListBuffer<>(); |
1841 if (tree.name != names.empty) { |
1843 if (tree.name != names.empty) { |
1842 firstadr = nextadr; |
1844 firstadr = nextadr; |
1843 } |
1845 } |
1968 checkInit(TreeInfo.diagEndPos(tree.body), var); |
1970 checkInit(TreeInfo.diagEndPos(tree.body), var); |
1969 } |
1971 } |
1970 } |
1972 } |
1971 } |
1973 } |
1972 } |
1974 } |
1973 List<AssignPendingExit> exits = pendingExits.toList(); |
1975 List<PendingExit> exits = pendingExits.toList(); |
1974 pendingExits = new ListBuffer<>(); |
1976 pendingExits = new ListBuffer<>(); |
1975 while (exits.nonEmpty()) { |
1977 while (exits.nonEmpty()) { |
1976 AssignPendingExit exit = exits.head; |
1978 PendingExit exit = exits.head; |
1977 exits = exits.tail; |
1979 exits = exits.tail; |
1978 Assert.check(exit.tree.hasTag(RETURN), exit.tree); |
1980 Assert.check(exit.tree.hasTag(RETURN), exit.tree); |
1979 if (isInitialConstructor) { |
1981 if (isInitialConstructor) { |
1980 inits.assign(exit.exit_inits); |
1982 Assert.check(exit instanceof AssignPendingExit); |
|
1983 inits.assign(((AssignPendingExit) exit).exit_inits); |
1981 for (int i = firstadr; i < nextadr; i++) { |
1984 for (int i = firstadr; i < nextadr; i++) { |
1982 checkInit(exit.tree.pos(), vardecls[i].sym); |
1985 checkInit(exit.tree.pos(), vardecls[i].sym); |
1983 } |
1986 } |
1984 } |
1987 } |
1985 } |
1988 } |
2025 scan(tree.stats); |
2028 scan(tree.stats); |
2026 nextadr = nextadrPrev; |
2029 nextadr = nextadrPrev; |
2027 } |
2030 } |
2028 |
2031 |
2029 public void visitDoLoop(JCDoWhileLoop tree) { |
2032 public void visitDoLoop(JCDoWhileLoop tree) { |
2030 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
2033 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
2031 FlowKind prevFlowKind = flowKind; |
2034 FlowKind prevFlowKind = flowKind; |
2032 flowKind = FlowKind.NORMAL; |
2035 flowKind = FlowKind.NORMAL; |
2033 final Bits initsSkip = new Bits(true); |
2036 final Bits initsSkip = new Bits(true); |
2034 final Bits uninitsSkip = new Bits(true); |
2037 final Bits uninitsSkip = new Bits(true); |
2035 pendingExits = new ListBuffer<>(); |
2038 pendingExits = new ListBuffer<>(); |
2057 uninits.assign(uninitsSkip); |
2060 uninits.assign(uninitsSkip); |
2058 resolveBreaks(tree, prevPendingExits); |
2061 resolveBreaks(tree, prevPendingExits); |
2059 } |
2062 } |
2060 |
2063 |
2061 public void visitWhileLoop(JCWhileLoop tree) { |
2064 public void visitWhileLoop(JCWhileLoop tree) { |
2062 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
2065 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
2063 FlowKind prevFlowKind = flowKind; |
2066 FlowKind prevFlowKind = flowKind; |
2064 flowKind = FlowKind.NORMAL; |
2067 flowKind = FlowKind.NORMAL; |
2065 final Bits initsSkip = new Bits(true); |
2068 final Bits initsSkip = new Bits(true); |
2066 final Bits uninitsSkip = new Bits(true); |
2069 final Bits uninitsSkip = new Bits(true); |
2067 pendingExits = new ListBuffer<>(); |
2070 pendingExits = new ListBuffer<>(); |
2093 uninits.assign(uninitsSkip); |
2096 uninits.assign(uninitsSkip); |
2094 resolveBreaks(tree, prevPendingExits); |
2097 resolveBreaks(tree, prevPendingExits); |
2095 } |
2098 } |
2096 |
2099 |
2097 public void visitForLoop(JCForLoop tree) { |
2100 public void visitForLoop(JCForLoop tree) { |
2098 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
2101 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
2099 FlowKind prevFlowKind = flowKind; |
2102 FlowKind prevFlowKind = flowKind; |
2100 flowKind = FlowKind.NORMAL; |
2103 flowKind = FlowKind.NORMAL; |
2101 int nextadrPrev = nextadr; |
2104 int nextadrPrev = nextadr; |
2102 scan(tree.init); |
2105 scan(tree.init); |
2103 final Bits initsSkip = new Bits(true); |
2106 final Bits initsSkip = new Bits(true); |
2141 } |
2144 } |
2142 |
2145 |
2143 public void visitForeachLoop(JCEnhancedForLoop tree) { |
2146 public void visitForeachLoop(JCEnhancedForLoop tree) { |
2144 visitVarDef(tree.var); |
2147 visitVarDef(tree.var); |
2145 |
2148 |
2146 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
2149 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
2147 FlowKind prevFlowKind = flowKind; |
2150 FlowKind prevFlowKind = flowKind; |
2148 flowKind = FlowKind.NORMAL; |
2151 flowKind = FlowKind.NORMAL; |
2149 int nextadrPrev = nextadr; |
2152 int nextadrPrev = nextadr; |
2150 scan(tree.expr); |
2153 scan(tree.expr); |
2151 final Bits initsStart = new Bits(inits); |
2154 final Bits initsStart = new Bits(inits); |
2172 resolveBreaks(tree, prevPendingExits); |
2175 resolveBreaks(tree, prevPendingExits); |
2173 nextadr = nextadrPrev; |
2176 nextadr = nextadrPrev; |
2174 } |
2177 } |
2175 |
2178 |
2176 public void visitLabelled(JCLabeledStatement tree) { |
2179 public void visitLabelled(JCLabeledStatement tree) { |
2177 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
2180 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
2178 pendingExits = new ListBuffer<>(); |
2181 pendingExits = new ListBuffer<>(); |
2179 scan(tree.body); |
2182 scan(tree.body); |
2180 resolveBreaks(tree, prevPendingExits); |
2183 resolveBreaks(tree, prevPendingExits); |
2181 } |
2184 } |
2182 |
2185 |
2187 public void visitSwitchExpression(JCSwitchExpression tree) { |
2190 public void visitSwitchExpression(JCSwitchExpression tree) { |
2188 handleSwitch(tree, tree.selector, tree.cases); |
2191 handleSwitch(tree, tree.selector, tree.cases); |
2189 } |
2192 } |
2190 |
2193 |
2191 private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) { |
2194 private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) { |
2192 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
2195 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
2193 pendingExits = new ListBuffer<>(); |
2196 pendingExits = new ListBuffer<>(); |
2194 int nextadrPrev = nextadr; |
2197 int nextadrPrev = nextadr; |
2195 scanExpr(selector); |
2198 scanExpr(selector); |
2196 final Bits initsSwitch = new Bits(inits); |
2199 final Bits initsSwitch = new Bits(inits); |
2197 final Bits uninitsSwitch = new Bits(uninits); |
2200 final Bits uninitsSwitch = new Bits(uninits); |
2243 } |
2246 } |
2244 |
2247 |
2245 public void visitTry(JCTry tree) { |
2248 public void visitTry(JCTry tree) { |
2246 ListBuffer<JCVariableDecl> resourceVarDecls = new ListBuffer<>(); |
2249 ListBuffer<JCVariableDecl> resourceVarDecls = new ListBuffer<>(); |
2247 final Bits uninitsTryPrev = new Bits(uninitsTry); |
2250 final Bits uninitsTryPrev = new Bits(uninitsTry); |
2248 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
2251 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
2249 pendingExits = new ListBuffer<>(); |
2252 pendingExits = new ListBuffer<>(); |
2250 final Bits initsTry = new Bits(inits); |
2253 final Bits initsTry = new Bits(inits); |
2251 uninitsTry.assign(uninits); |
2254 uninitsTry.assign(uninits); |
2252 for (JCTree resource : tree.resources) { |
2255 for (JCTree resource : tree.resources) { |
2253 if (resource instanceof JCVariableDecl) { |
2256 if (resource instanceof JCVariableDecl) { |
2300 nextadr = nextadrCatch; |
2303 nextadr = nextadrCatch; |
2301 } |
2304 } |
2302 if (tree.finalizer != null) { |
2305 if (tree.finalizer != null) { |
2303 inits.assign(initsTry); |
2306 inits.assign(initsTry); |
2304 uninits.assign(uninitsTry); |
2307 uninits.assign(uninitsTry); |
2305 ListBuffer<AssignPendingExit> exits = pendingExits; |
2308 ListBuffer<PendingExit> exits = pendingExits; |
2306 pendingExits = prevPendingExits; |
2309 pendingExits = prevPendingExits; |
2307 scan(tree.finalizer); |
2310 scan(tree.finalizer); |
2308 if (!tree.finallyCanCompleteNormally) { |
2311 if (!tree.finallyCanCompleteNormally) { |
2309 // discard exits and exceptions from try and finally |
2312 // discard exits and exceptions from try and finally |
2310 } else { |
2313 } else { |
2311 uninits.andSet(uninitsEnd); |
2314 uninits.andSet(uninitsEnd); |
2312 // FIX: this doesn't preserve source order of exits in catch |
2315 // FIX: this doesn't preserve source order of exits in catch |
2313 // versus finally! |
2316 // versus finally! |
2314 while (exits.nonEmpty()) { |
2317 while (exits.nonEmpty()) { |
2315 AssignPendingExit exit = exits.next(); |
2318 PendingExit exit = exits.next(); |
2316 if (exit.exit_inits != null) { |
2319 if (exit instanceof AssignPendingExit) { |
2317 exit.exit_inits.orSet(inits); |
2320 ((AssignPendingExit) exit).exit_inits.orSet(inits); |
2318 exit.exit_uninits.andSet(uninits); |
2321 ((AssignPendingExit) exit).exit_uninits.andSet(uninits); |
2319 } |
2322 } |
2320 pendingExits.append(exit); |
2323 pendingExits.append(exit); |
2321 } |
2324 } |
2322 inits.orSet(initsEnd); |
2325 inits.orSet(initsEnd); |
2323 } |
2326 } |
2324 } else { |
2327 } else { |
2325 inits.assign(initsEnd); |
2328 inits.assign(initsEnd); |
2326 uninits.assign(uninitsEnd); |
2329 uninits.assign(uninitsEnd); |
2327 ListBuffer<AssignPendingExit> exits = pendingExits; |
2330 ListBuffer<PendingExit> exits = pendingExits; |
2328 pendingExits = prevPendingExits; |
2331 pendingExits = prevPendingExits; |
2329 while (exits.nonEmpty()) pendingExits.append(exits.next()); |
2332 while (exits.nonEmpty()) pendingExits.append(exits.next()); |
2330 } |
2333 } |
2331 uninitsTry.andSet(uninitsTryPrev).andSet(uninits); |
2334 uninitsTry.andSet(uninitsTryPrev).andSet(uninits); |
2332 } |
2335 } |
2388 } |
2391 } |
2389 } |
2392 } |
2390 |
2393 |
2391 @Override |
2394 @Override |
2392 public void visitBreak(JCBreak tree) { |
2395 public void visitBreak(JCBreak tree) { |
2393 if (tree.isValueBreak()) |
2396 if (tree.isValueBreak()) { |
|
2397 if (tree.target.hasTag(SWITCH_EXPRESSION)) { |
|
2398 JCSwitchExpression expr = (JCSwitchExpression) tree.target; |
|
2399 if (expr.type.hasTag(BOOLEAN)) { |
|
2400 scanCond(tree.value); |
|
2401 Bits initsAfterBreakWhenTrue = new Bits(initsWhenTrue); |
|
2402 Bits initsAfterBreakWhenFalse = new Bits(initsWhenFalse); |
|
2403 Bits uninitsAfterBreakWhenTrue = new Bits(uninitsWhenTrue); |
|
2404 Bits uninitsAfterBreakWhenFalse = new Bits(uninitsWhenFalse); |
|
2405 PendingExit exit = new PendingExit(tree) { |
|
2406 @Override |
|
2407 void resolveJump() { |
|
2408 if (!inits.isReset()) { |
|
2409 split(true); |
|
2410 } |
|
2411 initsWhenTrue.andSet(initsAfterBreakWhenTrue); |
|
2412 initsWhenFalse.andSet(initsAfterBreakWhenFalse); |
|
2413 uninitsWhenTrue.andSet(uninitsAfterBreakWhenTrue); |
|
2414 uninitsWhenFalse.andSet(uninitsAfterBreakWhenFalse); |
|
2415 } |
|
2416 }; |
|
2417 merge(); |
|
2418 recordExit(exit); |
|
2419 return ; |
|
2420 } |
|
2421 } |
2394 scan(tree.value); |
2422 scan(tree.value); |
|
2423 } |
2395 recordExit(new AssignPendingExit(tree, inits, uninits)); |
2424 recordExit(new AssignPendingExit(tree, inits, uninits)); |
2396 } |
2425 } |
2397 |
2426 |
2398 @Override |
2427 @Override |
2399 public void visitContinue(JCContinue tree) { |
2428 public void visitContinue(JCContinue tree) { |
2426 public void visitLambda(JCLambda tree) { |
2455 public void visitLambda(JCLambda tree) { |
2427 final Bits prevUninits = new Bits(uninits); |
2456 final Bits prevUninits = new Bits(uninits); |
2428 final Bits prevInits = new Bits(inits); |
2457 final Bits prevInits = new Bits(inits); |
2429 int returnadrPrev = returnadr; |
2458 int returnadrPrev = returnadr; |
2430 int nextadrPrev = nextadr; |
2459 int nextadrPrev = nextadr; |
2431 ListBuffer<AssignPendingExit> prevPending = pendingExits; |
2460 ListBuffer<PendingExit> prevPending = pendingExits; |
2432 try { |
2461 try { |
2433 returnadr = nextadr; |
2462 returnadr = nextadr; |
2434 pendingExits = new ListBuffer<>(); |
2463 pendingExits = new ListBuffer<>(); |
2435 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { |
2464 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { |
2436 JCVariableDecl def = l.head; |
2465 JCVariableDecl def = l.head; |
2616 * Additional this also checks that every variable that is used as an operand to |
2645 * Additional this also checks that every variable that is used as an operand to |
2617 * try-with-resources is final or effectively final. |
2646 * try-with-resources is final or effectively final. |
2618 * As effectively final variables are marked as such during DA/DU, this pass must run after |
2647 * As effectively final variables are marked as such during DA/DU, this pass must run after |
2619 * AssignAnalyzer. |
2648 * AssignAnalyzer. |
2620 */ |
2649 */ |
2621 class CaptureAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> { |
2650 class CaptureAnalyzer extends BaseAnalyzer { |
2622 |
2651 |
2623 JCTree currentTree; //local class or lambda |
2652 JCTree currentTree; //local class or lambda |
2624 |
2653 |
2625 @Override |
2654 @Override |
2626 void markDead() { |
2655 void markDead() { |