@@ -952,6 +952,10 @@ namespace ts {
952
952
return initFlowNode ( { flags : FlowFlags . LoopLabel , antecedents : undefined } ) ;
953
953
}
954
954
955
+ function createReduceLabel ( target : FlowLabel , antecedents : FlowNode [ ] , antecedent : FlowNode ) : FlowReduceLabel {
956
+ return initFlowNode ( { flags : FlowFlags . ReduceLabel , target, antecedents, antecedent } ) ;
957
+ }
958
+
955
959
function setFlowNodeReferenced ( flow : FlowNode ) {
956
960
// On first reference we set the Referenced flag, thereafter we set the Shared flag
957
961
flow . flags |= flow . flags & FlowFlags . Referenced ? FlowFlags . Shared : FlowFlags . Referenced ;
@@ -1209,35 +1213,36 @@ namespace ts {
1209
1213
}
1210
1214
1211
1215
function bindTryStatement ( node : TryStatement ) : void {
1212
- const preFinallyLabel = createBranchLabel ( ) ;
1213
1216
// We conservatively assume that *any* code in the try block can cause an exception, but we only need
1214
1217
// to track code that causes mutations (because only mutations widen the possible control flow type of
1215
- // a variable). The currentExceptionTarget is the target label for control flows that result from
1216
- // exceptions. We add all mutation flow nodes as antecedents of this label such that we can analyze them
1217
- // as possible antecedents of the start of catch or finally blocks. Furthermore, we add the current
1218
- // control flow to represent exceptions that occur before any mutations.
1218
+ // a variable). The exceptionLabel is the target label for control flows that result from exceptions.
1219
+ // We add all mutation flow nodes as antecedents of this label such that we can analyze them as possible
1220
+ // antecedents of the start of catch or finally blocks. Furthermore, we add the current control flow to
1221
+ // represent exceptions that occur before any mutations.
1219
1222
const saveReturnTarget = currentReturnTarget ;
1220
1223
const saveExceptionTarget = currentExceptionTarget ;
1221
- currentReturnTarget = createBranchLabel ( ) ;
1222
- currentExceptionTarget = node . catchClause ? createBranchLabel ( ) : currentReturnTarget ;
1223
- addAntecedent ( currentExceptionTarget , currentFlow ) ;
1224
+ const normalExitLabel = createBranchLabel ( ) ;
1225
+ const returnLabel = createBranchLabel ( ) ;
1226
+ let exceptionLabel = createBranchLabel ( ) ;
1227
+ if ( node . finallyBlock ) {
1228
+ currentReturnTarget = returnLabel ;
1229
+ }
1230
+ addAntecedent ( exceptionLabel , currentFlow ) ;
1231
+ currentExceptionTarget = exceptionLabel ;
1224
1232
bind ( node . tryBlock ) ;
1225
- addAntecedent ( preFinallyLabel , currentFlow ) ;
1226
- const flowAfterTry = currentFlow ;
1227
- let flowAfterCatch = unreachableFlow ;
1233
+ addAntecedent ( normalExitLabel , currentFlow ) ;
1228
1234
if ( node . catchClause ) {
1229
1235
// Start of catch clause is the target of exceptions from try block.
1230
- currentFlow = finishFlowLabel ( currentExceptionTarget ) ;
1236
+ currentFlow = finishFlowLabel ( exceptionLabel ) ;
1231
1237
// The currentExceptionTarget now represents control flows from exceptions in the catch clause.
1232
1238
// Effectively, in a try-catch-finally, if an exception occurs in the try block, the catch block
1233
1239
// acts like a second try block.
1234
- currentExceptionTarget = currentReturnTarget ;
1235
- addAntecedent ( currentExceptionTarget , currentFlow ) ;
1240
+ exceptionLabel = createBranchLabel ( ) ;
1241
+ addAntecedent ( exceptionLabel , currentFlow ) ;
1242
+ currentExceptionTarget = exceptionLabel ;
1236
1243
bind ( node . catchClause ) ;
1237
- addAntecedent ( preFinallyLabel , currentFlow ) ;
1238
- flowAfterCatch = currentFlow ;
1244
+ addAntecedent ( normalExitLabel , currentFlow ) ;
1239
1245
}
1240
- const exceptionTarget = finishFlowLabel ( currentExceptionTarget ) ;
1241
1246
currentReturnTarget = saveReturnTarget ;
1242
1247
currentExceptionTarget = saveExceptionTarget ;
1243
1248
if ( node . finallyBlock ) {
@@ -1250,35 +1255,33 @@ namespace ts {
1250
1255
// When analyzing a control flow graph that starts inside a finally block we want to consider all
1251
1256
// five possibilities above. However, when analyzing a control flow graph that starts outside (past)
1252
1257
// the finally block, we only want to consider the first two (if we're past a finally block then it
1253
- // must have completed normally). To make this possible, we inject two extra nodes into the control
1254
- // flow graph: An after-finally with an antecedent of the control flow at the end of the finally
1255
- // block, and a pre-finally with an antecedent that represents all exceptional control flows. The
1256
- // 'lock' property of the pre-finally references the after-finally, and the after-finally has a
1257
- // boolean 'locked' property that we set to true when analyzing a control flow that contained the
1258
- // the after-finally node. When the lock associated with a pre-finally is locked, the antecedent of
1259
- // the pre-finally (i.e. the exceptional control flows) are skipped.
1260
- const preFinallyFlow : PreFinallyFlow = initFlowNode ( { flags : FlowFlags . PreFinally , antecedent : exceptionTarget , lock : { } } ) ;
1261
- addAntecedent ( preFinallyLabel , preFinallyFlow ) ;
1262
- currentFlow = finishFlowLabel ( preFinallyLabel ) ;
1258
+ // must have completed normally). Likewise, when analyzing a control flow graph from return statements
1259
+ // in try or catch blocks in an IIFE, we only want to consider the third. To make this possible, we
1260
+ // inject a ReduceLabel node into the control flow graph. This node contains an alternate reduced
1261
+ // set of antecedents for the pre-finally label. As control flow analysis passes by a ReduceLabel
1262
+ // node, the pre-finally label is temporarily switched to the reduced antecedent set.
1263
+ const finallyLabel = createBranchLabel ( ) ;
1264
+ finallyLabel . antecedents = concatenate ( concatenate ( normalExitLabel . antecedents , exceptionLabel . antecedents ) , returnLabel . antecedents ) ;
1265
+ currentFlow = finallyLabel ;
1263
1266
bind ( node . finallyBlock ) ;
1264
- // If the end of the finally block is reachable, but the end of the try and catch blocks are not,
1265
- // convert the current flow to unreachable. For example, 'try { return 1; } finally { ... }' should
1266
- // result in an unreachable current control flow.
1267
- if ( ! ( currentFlow . flags & FlowFlags . Unreachable ) ) {
1268
- if ( ( flowAfterTry . flags & FlowFlags . Unreachable ) && ( flowAfterCatch . flags & FlowFlags . Unreachable ) ) {
1269
- currentFlow = flowAfterTry === reportedUnreachableFlow || flowAfterCatch === reportedUnreachableFlow
1270
- ? reportedUnreachableFlow
1271
- : unreachableFlow ;
1272
- }
1267
+ if ( currentFlow . flags & FlowFlags . Unreachable ) {
1268
+ // If the end of the finally block is unreachable, the end of the entire try statement is unreachable.
1269
+ currentFlow = unreachableFlow ;
1273
1270
}
1274
- if ( ! ( currentFlow . flags & FlowFlags . Unreachable ) ) {
1275
- const afterFinallyFlow : AfterFinallyFlow = initFlowNode ( { flags : FlowFlags . AfterFinally , antecedent : currentFlow } ) ;
1276
- preFinallyFlow . lock = afterFinallyFlow ;
1277
- currentFlow = afterFinallyFlow ;
1271
+ else {
1272
+ // If we have an IIFE return target and return statements in the try or catch blocks, add a control
1273
+ // flow that goes back through the finally block and back through only the return statements.
1274
+ if ( currentReturnTarget && returnLabel . antecedents ) {
1275
+ addAntecedent ( currentReturnTarget , createReduceLabel ( finallyLabel , returnLabel . antecedents , currentFlow ) ) ;
1276
+ }
1277
+ // If the end of the finally block is reachable, but the end of the try and catch blocks are not,
1278
+ // convert the current flow to unreachable. For example, 'try { return 1; } finally { ... }' should
1279
+ // result in an unreachable current control flow.
1280
+ currentFlow = normalExitLabel . antecedents ? createReduceLabel ( finallyLabel , normalExitLabel . antecedents , currentFlow ) : unreachableFlow ;
1278
1281
}
1279
1282
}
1280
1283
else {
1281
- currentFlow = finishFlowLabel ( preFinallyLabel ) ;
1284
+ currentFlow = finishFlowLabel ( normalExitLabel ) ;
1282
1285
}
1283
1286
}
1284
1287
0 commit comments