< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java

Print this page




 183  *  support for assigning to a final field via this.x.
 184  *
 185  *  <p><b>This is NOT part of any supported API.
 186  *  If you write code that depends on this, you do so at your own risk.
 187  *  This code and its internal interfaces are subject to change or
 188  *  deletion without notice.</b>
 189  */
 190 public class Flow {
 191     protected static final Context.Key<Flow> flowKey = new Context.Key<>();
 192 
 193     private final Names names;
 194     private final Log log;
 195     private final Symtab syms;
 196     private final Types types;
 197     private final Check chk;
 198     private       TreeMaker make;
 199     private final Resolve rs;
 200     private final JCDiagnostic.Factory diags;
 201     private Env<AttrContext> attrEnv;
 202     private       Lint lint;
 203     private final boolean allowImprovedRethrowAnalysis;
 204     private final boolean allowImprovedCatchAnalysis;
 205     private final boolean allowEffectivelyFinalInInnerClasses;
 206     private final boolean enforceThisDotInit;
 207 
 208     public static Flow instance(Context context) {
 209         Flow instance = context.get(flowKey);
 210         if (instance == null)
 211             instance = new Flow(context);
 212         return instance;
 213     }
 214 
 215     public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
 216         new AliveAnalyzer().analyzeTree(env, make);
 217         new AssignAnalyzer().analyzeTree(env);
 218         new FlowAnalyzer().analyzeTree(env, make);
 219         new CaptureAnalyzer().analyzeTree(env, make);
 220     }
 221 
 222     public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) {
 223         Log.DiagnosticHandler diagHandler = null;
 224         //we need to disable diagnostics temporarily; the problem is that if
 225         //a lambda expression contains e.g. an unreachable statement, an error
 226         //message will be reported and will cause compilation to skip the flow analyis


 277             this.errKey = errKey;
 278             this.isFinal = isFinal;
 279         }
 280 
 281         boolean isFinal() {
 282             return isFinal;
 283         }
 284     }
 285 
 286     protected Flow(Context context) {
 287         context.put(flowKey, this);
 288         names = Names.instance(context);
 289         log = Log.instance(context);
 290         syms = Symtab.instance(context);
 291         types = Types.instance(context);
 292         chk = Check.instance(context);
 293         lint = Lint.instance(context);
 294         rs = Resolve.instance(context);
 295         diags = JCDiagnostic.Factory.instance(context);
 296         Source source = Source.instance(context);
 297         allowImprovedRethrowAnalysis = Feature.IMPROVED_RETHROW_ANALYSIS.allowedInSource(source);
 298         allowImprovedCatchAnalysis = Feature.IMPROVED_CATCH_ANALYSIS.allowedInSource(source);
 299         allowEffectivelyFinalInInnerClasses = Feature.EFFECTIVELY_FINAL_IN_INNER_CLASSES.allowedInSource(source);
 300         enforceThisDotInit = Feature.ENFORCE_THIS_DOT_INIT.allowedInSource(source);
 301     }
 302 
 303     /**
 304      * Base visitor class for all visitors implementing dataflow analysis logic.
 305      * This class define the shared logic for handling jumps (break/continue statements).
 306      */
 307     static abstract class BaseAnalyzer<P extends BaseAnalyzer.PendingExit> extends TreeScanner {
 308 
 309         enum JumpKind {
 310             BREAK(JCTree.Tag.BREAK) {
 311                 @Override
 312                 JCTree getTarget(JCTree tree) {
 313                     return ((JCBreak)tree).target;
 314                 }
 315             },
 316             CONTINUE(JCTree.Tag.CONTINUE) {
 317                 @Override
 318                 JCTree getTarget(JCTree tree) {
 319                     return ((JCContinue)tree).target;
 320                 }


1089                     types.interfaces(resource.type).prepend(types.supertype(resource.type)) :
1090                     List.of(resource.type);
1091                 for (Type sup : closeableSupertypes) {
1092                     if (types.asSuper(sup, syms.autoCloseableType.tsym) != null) {
1093                         Symbol closeMethod = rs.resolveQualifiedMethod(tree,
1094                                 attrEnv,
1095                                 types.skipTypeVars(sup, false),
1096                                 names.close,
1097                                 List.nil(),
1098                                 List.nil());
1099                         Type mt = types.memberType(resource.type, closeMethod);
1100                         if (closeMethod.kind == MTH) {
1101                             for (Type t : mt.getThrownTypes()) {
1102                                 markThrown(resource, t);
1103                             }
1104                         }
1105                     }
1106                 }
1107             }
1108             scan(tree.body);
1109             List<Type> thrownInTry = allowImprovedCatchAnalysis ?
1110                 chk.union(thrown, List.of(syms.runtimeExceptionType, syms.errorType)) :
1111                 thrown;
1112             thrown = thrownPrev;
1113             caught = caughtPrev;
1114 
1115             List<Type> caughtInTry = List.nil();
1116             for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
1117                 JCVariableDecl param = l.head.param;
1118                 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ?
1119                         ((JCTypeUnion)l.head.param.vartype).alternatives :
1120                         List.of(l.head.param.vartype);
1121                 List<Type> ctypes = List.nil();
1122                 List<Type> rethrownTypes = chk.diff(thrownInTry, caughtInTry);
1123                 for (JCExpression ct : subClauses) {
1124                     Type exc = ct.type;
1125                     if (exc != syms.unknownType) {
1126                         ctypes = ctypes.append(exc);
1127                         if (types.isSameType(exc, syms.objectType))
1128                             continue;
1129                         checkCaughtType(l.head.pos(), exc, thrownInTry, caughtInTry);
1130                         caughtInTry = chk.incl(exc, caughtInTry);
1131                     }


1160                 while (exits.nonEmpty()) pendingExits.append(exits.next());
1161             }
1162         }
1163 
1164         @Override
1165         public void visitIf(JCIf tree) {
1166             scan(tree.cond);
1167             scan(tree.thenpart);
1168             if (tree.elsepart != null) {
1169                 scan(tree.elsepart);
1170             }
1171         }
1172 
1173         void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) {
1174             if (chk.subset(exc, caughtInTry)) {
1175                 log.error(pos, Errors.ExceptAlreadyCaught(exc));
1176             } else if (!chk.isUnchecked(pos, exc) &&
1177                     !isExceptionOrThrowable(exc) &&
1178                     !chk.intersects(exc, thrownInTry)) {
1179                 log.error(pos, Errors.ExceptNeverThrownInTry(exc));
1180             } else if (allowImprovedCatchAnalysis) {
1181                 List<Type> catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry);
1182                 // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an
1183                 // unchecked exception, the result list would not be empty, as the augmented
1184                 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked
1185                 // exception, that would have been covered in the branch above
1186                 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() &&
1187                         !isExceptionOrThrowable(exc)) {
1188                     Warning key = catchableThrownTypes.length() == 1 ?
1189                             Warnings.UnreachableCatch(catchableThrownTypes) :
1190                             Warnings.UnreachableCatch1(catchableThrownTypes);
1191                     log.warning(pos, key);
1192                 }
1193             }
1194         }
1195         //where
1196             private boolean isExceptionOrThrowable(Type exc) {
1197                 return exc.tsym == syms.throwableType.tsym ||
1198                     exc.tsym == syms.exceptionType.tsym;
1199             }
1200 
1201         public void visitBreak(JCBreak tree) {
1202             recordExit(new FlowPendingExit(tree, null));
1203         }
1204 
1205         public void visitContinue(JCContinue tree) {
1206             recordExit(new FlowPendingExit(tree, null));
1207         }
1208 
1209         public void visitReturn(JCReturn tree) {
1210             scan(tree.expr);
1211             recordExit(new FlowPendingExit(tree, null));
1212         }
1213 
1214         public void visitThrow(JCThrow tree) {
1215             scan(tree.expr);
1216             Symbol sym = TreeInfo.symbol(tree.expr);
1217             if (sym != null &&
1218                 sym.kind == VAR &&
1219                 (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 &&
1220                 preciseRethrowTypes.get(sym) != null &&
1221                 allowImprovedRethrowAnalysis) {
1222                 for (Type t : preciseRethrowTypes.get(sym)) {
1223                     markThrown(tree, t);
1224                 }
1225             }
1226             else {
1227                 markThrown(tree, tree.expr.type);
1228             }
1229             markDead();
1230         }
1231 
1232         public void visitApply(JCMethodInvocation tree) {
1233             scan(tree.meth);
1234             scan(tree.args);
1235             for (List<Type> l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail)
1236                 markThrown(tree, l.head);
1237         }
1238 
1239         public void visitNewClass(JCNewClass tree) {
1240             scan(tree.encl);
1241             scan(tree.args);


2384             if (tree.detail != null) {
2385                 inits.assign(initsWhenFalse);
2386                 uninits.assign(uninitsWhenFalse);
2387                 scanExpr(tree.detail);
2388             }
2389             inits.assign(initsExit);
2390             uninits.assign(uninitsExit);
2391         }
2392 
2393         public void visitAssign(JCAssign tree) {
2394             if (!TreeInfo.isIdentOrThisDotIdent(tree.lhs))
2395                 scanExpr(tree.lhs);
2396             scanExpr(tree.rhs);
2397             letInit(tree.lhs);
2398         }
2399 
2400         // check fields accessed through this.<field> are definitely
2401         // assigned before reading their value
2402         public void visitSelect(JCFieldAccess tree) {
2403             super.visitSelect(tree);
2404             if (enforceThisDotInit &&
2405                     TreeInfo.isThisQualifier(tree.selected) &&
2406                     tree.sym.kind == VAR) {
2407                 checkInit(tree.pos(), (VarSymbol)tree.sym);
2408             }
2409         }
2410 
2411         public void visitAssignop(JCAssignOp tree) {
2412             scanExpr(tree.lhs);
2413             scanExpr(tree.rhs);
2414             letInit(tree.lhs);
2415         }
2416 
2417         public void visitUnary(JCUnary tree) {
2418             switch (tree.getTag()) {
2419             case NOT:
2420                 scanCond(tree.arg);
2421                 final Bits t = new Bits(initsWhenFalse);
2422                 initsWhenFalse.assign(initsWhenTrue);
2423                 initsWhenTrue.assign(t);
2424                 t.assign(uninitsWhenFalse);
2425                 uninitsWhenFalse.assign(uninitsWhenTrue);




 183  *  support for assigning to a final field via this.x.
 184  *
 185  *  <p><b>This is NOT part of any supported API.
 186  *  If you write code that depends on this, you do so at your own risk.
 187  *  This code and its internal interfaces are subject to change or
 188  *  deletion without notice.</b>
 189  */
 190 public class Flow {
 191     protected static final Context.Key<Flow> flowKey = new Context.Key<>();
 192 
 193     private final Names names;
 194     private final Log log;
 195     private final Symtab syms;
 196     private final Types types;
 197     private final Check chk;
 198     private       TreeMaker make;
 199     private final Resolve rs;
 200     private final JCDiagnostic.Factory diags;
 201     private Env<AttrContext> attrEnv;
 202     private       Lint lint;


 203     private final boolean allowEffectivelyFinalInInnerClasses;

 204 
 205     public static Flow instance(Context context) {
 206         Flow instance = context.get(flowKey);
 207         if (instance == null)
 208             instance = new Flow(context);
 209         return instance;
 210     }
 211 
 212     public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
 213         new AliveAnalyzer().analyzeTree(env, make);
 214         new AssignAnalyzer().analyzeTree(env);
 215         new FlowAnalyzer().analyzeTree(env, make);
 216         new CaptureAnalyzer().analyzeTree(env, make);
 217     }
 218 
 219     public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) {
 220         Log.DiagnosticHandler diagHandler = null;
 221         //we need to disable diagnostics temporarily; the problem is that if
 222         //a lambda expression contains e.g. an unreachable statement, an error
 223         //message will be reported and will cause compilation to skip the flow analyis


 274             this.errKey = errKey;
 275             this.isFinal = isFinal;
 276         }
 277 
 278         boolean isFinal() {
 279             return isFinal;
 280         }
 281     }
 282 
 283     protected Flow(Context context) {
 284         context.put(flowKey, this);
 285         names = Names.instance(context);
 286         log = Log.instance(context);
 287         syms = Symtab.instance(context);
 288         types = Types.instance(context);
 289         chk = Check.instance(context);
 290         lint = Lint.instance(context);
 291         rs = Resolve.instance(context);
 292         diags = JCDiagnostic.Factory.instance(context);
 293         Source source = Source.instance(context);


 294         allowEffectivelyFinalInInnerClasses = Feature.EFFECTIVELY_FINAL_IN_INNER_CLASSES.allowedInSource(source);

 295     }
 296 
 297     /**
 298      * Base visitor class for all visitors implementing dataflow analysis logic.
 299      * This class define the shared logic for handling jumps (break/continue statements).
 300      */
 301     static abstract class BaseAnalyzer<P extends BaseAnalyzer.PendingExit> extends TreeScanner {
 302 
 303         enum JumpKind {
 304             BREAK(JCTree.Tag.BREAK) {
 305                 @Override
 306                 JCTree getTarget(JCTree tree) {
 307                     return ((JCBreak)tree).target;
 308                 }
 309             },
 310             CONTINUE(JCTree.Tag.CONTINUE) {
 311                 @Override
 312                 JCTree getTarget(JCTree tree) {
 313                     return ((JCContinue)tree).target;
 314                 }


1083                     types.interfaces(resource.type).prepend(types.supertype(resource.type)) :
1084                     List.of(resource.type);
1085                 for (Type sup : closeableSupertypes) {
1086                     if (types.asSuper(sup, syms.autoCloseableType.tsym) != null) {
1087                         Symbol closeMethod = rs.resolveQualifiedMethod(tree,
1088                                 attrEnv,
1089                                 types.skipTypeVars(sup, false),
1090                                 names.close,
1091                                 List.nil(),
1092                                 List.nil());
1093                         Type mt = types.memberType(resource.type, closeMethod);
1094                         if (closeMethod.kind == MTH) {
1095                             for (Type t : mt.getThrownTypes()) {
1096                                 markThrown(resource, t);
1097                             }
1098                         }
1099                     }
1100                 }
1101             }
1102             scan(tree.body);
1103             List<Type> thrownInTry = chk.union(thrown, List.of(syms.runtimeExceptionType, syms.errorType));


1104             thrown = thrownPrev;
1105             caught = caughtPrev;
1106 
1107             List<Type> caughtInTry = List.nil();
1108             for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
1109                 JCVariableDecl param = l.head.param;
1110                 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ?
1111                         ((JCTypeUnion)l.head.param.vartype).alternatives :
1112                         List.of(l.head.param.vartype);
1113                 List<Type> ctypes = List.nil();
1114                 List<Type> rethrownTypes = chk.diff(thrownInTry, caughtInTry);
1115                 for (JCExpression ct : subClauses) {
1116                     Type exc = ct.type;
1117                     if (exc != syms.unknownType) {
1118                         ctypes = ctypes.append(exc);
1119                         if (types.isSameType(exc, syms.objectType))
1120                             continue;
1121                         checkCaughtType(l.head.pos(), exc, thrownInTry, caughtInTry);
1122                         caughtInTry = chk.incl(exc, caughtInTry);
1123                     }


1152                 while (exits.nonEmpty()) pendingExits.append(exits.next());
1153             }
1154         }
1155 
1156         @Override
1157         public void visitIf(JCIf tree) {
1158             scan(tree.cond);
1159             scan(tree.thenpart);
1160             if (tree.elsepart != null) {
1161                 scan(tree.elsepart);
1162             }
1163         }
1164 
1165         void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) {
1166             if (chk.subset(exc, caughtInTry)) {
1167                 log.error(pos, Errors.ExceptAlreadyCaught(exc));
1168             } else if (!chk.isUnchecked(pos, exc) &&
1169                     !isExceptionOrThrowable(exc) &&
1170                     !chk.intersects(exc, thrownInTry)) {
1171                 log.error(pos, Errors.ExceptNeverThrownInTry(exc));
1172             } else {
1173                 List<Type> catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry);
1174                 // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an
1175                 // unchecked exception, the result list would not be empty, as the augmented
1176                 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked
1177                 // exception, that would have been covered in the branch above
1178                 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() &&
1179                         !isExceptionOrThrowable(exc)) {
1180                     Warning key = catchableThrownTypes.length() == 1 ?
1181                             Warnings.UnreachableCatch(catchableThrownTypes) :
1182                             Warnings.UnreachableCatch1(catchableThrownTypes);
1183                     log.warning(pos, key);
1184                 }
1185             }
1186         }
1187         //where
1188             private boolean isExceptionOrThrowable(Type exc) {
1189                 return exc.tsym == syms.throwableType.tsym ||
1190                     exc.tsym == syms.exceptionType.tsym;
1191             }
1192 
1193         public void visitBreak(JCBreak tree) {
1194             recordExit(new FlowPendingExit(tree, null));
1195         }
1196 
1197         public void visitContinue(JCContinue tree) {
1198             recordExit(new FlowPendingExit(tree, null));
1199         }
1200 
1201         public void visitReturn(JCReturn tree) {
1202             scan(tree.expr);
1203             recordExit(new FlowPendingExit(tree, null));
1204         }
1205 
1206         public void visitThrow(JCThrow tree) {
1207             scan(tree.expr);
1208             Symbol sym = TreeInfo.symbol(tree.expr);
1209             if (sym != null &&
1210                 sym.kind == VAR &&
1211                 (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 &&
1212                 preciseRethrowTypes.get(sym) != null) {

1213                 for (Type t : preciseRethrowTypes.get(sym)) {
1214                     markThrown(tree, t);
1215                 }
1216             }
1217             else {
1218                 markThrown(tree, tree.expr.type);
1219             }
1220             markDead();
1221         }
1222 
1223         public void visitApply(JCMethodInvocation tree) {
1224             scan(tree.meth);
1225             scan(tree.args);
1226             for (List<Type> l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail)
1227                 markThrown(tree, l.head);
1228         }
1229 
1230         public void visitNewClass(JCNewClass tree) {
1231             scan(tree.encl);
1232             scan(tree.args);


2375             if (tree.detail != null) {
2376                 inits.assign(initsWhenFalse);
2377                 uninits.assign(uninitsWhenFalse);
2378                 scanExpr(tree.detail);
2379             }
2380             inits.assign(initsExit);
2381             uninits.assign(uninitsExit);
2382         }
2383 
2384         public void visitAssign(JCAssign tree) {
2385             if (!TreeInfo.isIdentOrThisDotIdent(tree.lhs))
2386                 scanExpr(tree.lhs);
2387             scanExpr(tree.rhs);
2388             letInit(tree.lhs);
2389         }
2390 
2391         // check fields accessed through this.<field> are definitely
2392         // assigned before reading their value
2393         public void visitSelect(JCFieldAccess tree) {
2394             super.visitSelect(tree);
2395             if (TreeInfo.isThisQualifier(tree.selected) &&

2396                 tree.sym.kind == VAR) {
2397                 checkInit(tree.pos(), (VarSymbol)tree.sym);
2398             }
2399         }
2400 
2401         public void visitAssignop(JCAssignOp tree) {
2402             scanExpr(tree.lhs);
2403             scanExpr(tree.rhs);
2404             letInit(tree.lhs);
2405         }
2406 
2407         public void visitUnary(JCUnary tree) {
2408             switch (tree.getTag()) {
2409             case NOT:
2410                 scanCond(tree.arg);
2411                 final Bits t = new Bits(initsWhenFalse);
2412                 initsWhenFalse.assign(initsWhenTrue);
2413                 initsWhenTrue.assign(t);
2414                 t.assign(uninitsWhenFalse);
2415                 uninitsWhenFalse.assign(uninitsWhenTrue);


< prev index next >