@@ -1445,28 +1445,156 @@ namespace ts {
1445
1445
}
1446
1446
}
1447
1447
1448
+ const enum BindBinaryExpressionFlowState {
1449
+ BindThenBindChildren ,
1450
+ MaybeBindLeft ,
1451
+ BindToken ,
1452
+ BindRight ,
1453
+ FinishBind
1454
+ }
1455
+
1448
1456
function bindBinaryExpressionFlow ( node : BinaryExpression ) {
1449
- const operator = node . operatorToken . kind ;
1450
- if ( operator === SyntaxKind . AmpersandAmpersandToken || operator === SyntaxKind . BarBarToken || operator === SyntaxKind . QuestionQuestionToken ) {
1451
- if ( isTopLevelLogicalExpression ( node ) ) {
1452
- const postExpressionLabel = createBranchLabel ( ) ;
1453
- bindLogicalExpression ( node , postExpressionLabel , postExpressionLabel ) ;
1454
- currentFlow = finishFlowLabel ( postExpressionLabel ) ;
1457
+ const workStacks : {
1458
+ expr : BinaryExpression [ ] ,
1459
+ state : BindBinaryExpressionFlowState [ ] ,
1460
+ inStrictMode : ( boolean | undefined ) [ ] ,
1461
+ parent : ( Node | undefined ) [ ] ,
1462
+ subtreeFlags : ( number | undefined ) [ ]
1463
+ } = {
1464
+ expr : [ node ] ,
1465
+ state : [ BindBinaryExpressionFlowState . MaybeBindLeft ] ,
1466
+ inStrictMode : [ undefined ] ,
1467
+ parent : [ undefined ] ,
1468
+ subtreeFlags : [ undefined ]
1469
+ } ;
1470
+ let stackIndex = 0 ;
1471
+ while ( stackIndex >= 0 ) {
1472
+ node = workStacks . expr [ stackIndex ] ;
1473
+ switch ( workStacks . state [ stackIndex ] ) {
1474
+ case BindBinaryExpressionFlowState . BindThenBindChildren : {
1475
+ // This state is used only when recuring, to emulate the work that `bind` does before
1476
+ // reaching `bindChildren`. A normal call to `bindBinaryExpressionFlow` will already have done this work.
1477
+ node . parent = parent ;
1478
+ const saveInStrictMode = inStrictMode ;
1479
+ bindWorker ( node ) ;
1480
+ const saveParent = parent ;
1481
+ parent = node ;
1482
+
1483
+ let subtreeFlagsState : number | undefined ;
1484
+ // While this next part does the work of `bindChildren` before it descends into `bindChildrenWorker`
1485
+ // and uses `subtreeFlagsState` to queue up the work that needs to be done once the node is bound.
1486
+ if ( skipTransformFlagAggregation ) {
1487
+ // do nothing extra
1488
+ }
1489
+ else if ( node . transformFlags & TransformFlags . HasComputedFlags ) {
1490
+ skipTransformFlagAggregation = true ;
1491
+ subtreeFlagsState = - 1 ;
1492
+ }
1493
+ else {
1494
+ const savedSubtreeTransformFlags = subtreeTransformFlags ;
1495
+ subtreeTransformFlags = 0 ;
1496
+ subtreeFlagsState = savedSubtreeTransformFlags ;
1497
+ }
1498
+
1499
+ advanceState ( BindBinaryExpressionFlowState . MaybeBindLeft , saveInStrictMode , saveParent , subtreeFlagsState ) ;
1500
+ break ;
1501
+ }
1502
+ case BindBinaryExpressionFlowState . MaybeBindLeft : {
1503
+ const operator = node . operatorToken . kind ;
1504
+ // TODO: bindLogicalExpression is recursive - if we want to handle deeply nested `&&` expressions
1505
+ // we'll need to handle the `bindLogicalExpression` scenarios in this state machine, too
1506
+ // For now, though, since the common cases are chained `+`, leaving it recursive is fine
1507
+ if ( operator === SyntaxKind . AmpersandAmpersandToken || operator === SyntaxKind . BarBarToken || operator === SyntaxKind . QuestionQuestionToken ) {
1508
+ if ( isTopLevelLogicalExpression ( node ) ) {
1509
+ const postExpressionLabel = createBranchLabel ( ) ;
1510
+ bindLogicalExpression ( node , postExpressionLabel , postExpressionLabel ) ;
1511
+ currentFlow = finishFlowLabel ( postExpressionLabel ) ;
1512
+ }
1513
+ else {
1514
+ bindLogicalExpression ( node , currentTrueTarget ! , currentFalseTarget ! ) ;
1515
+ }
1516
+ completeNode ( ) ;
1517
+ }
1518
+ else {
1519
+ advanceState ( BindBinaryExpressionFlowState . BindToken ) ;
1520
+ maybeBind ( node . left ) ;
1521
+ }
1522
+ break ;
1523
+ }
1524
+ case BindBinaryExpressionFlowState . BindToken : {
1525
+ advanceState ( BindBinaryExpressionFlowState . BindRight ) ;
1526
+ maybeBind ( node . operatorToken ) ;
1527
+ break ;
1528
+ }
1529
+ case BindBinaryExpressionFlowState . BindRight : {
1530
+ advanceState ( BindBinaryExpressionFlowState . FinishBind ) ;
1531
+ maybeBind ( node . right ) ;
1532
+ break ;
1533
+ }
1534
+ case BindBinaryExpressionFlowState . FinishBind : {
1535
+ const operator = node . operatorToken . kind ;
1536
+ if ( isAssignmentOperator ( operator ) && ! isAssignmentTarget ( node ) ) {
1537
+ bindAssignmentTargetFlow ( node . left ) ;
1538
+ if ( operator === SyntaxKind . EqualsToken && node . left . kind === SyntaxKind . ElementAccessExpression ) {
1539
+ const elementAccess = < ElementAccessExpression > node . left ;
1540
+ if ( isNarrowableOperand ( elementAccess . expression ) ) {
1541
+ currentFlow = createFlowMutation ( FlowFlags . ArrayMutation , currentFlow , node ) ;
1542
+ }
1543
+ }
1544
+ }
1545
+ completeNode ( ) ;
1546
+ break ;
1547
+ }
1548
+ default : return Debug . fail ( `Invalid state ${ workStacks . state [ stackIndex ] } for bindBinaryExpressionFlow` ) ;
1455
1549
}
1456
- else {
1457
- bindLogicalExpression ( node , currentTrueTarget ! , currentFalseTarget ! ) ;
1550
+ }
1551
+
1552
+ /**
1553
+ * Note that `advanceState` sets the _current_ head state, and that `maybeBind` potentially pushes on a new
1554
+ * head state; so `advanceState` must be called before any `maybeBind` during a state's execution.
1555
+ */
1556
+ function advanceState ( state : BindBinaryExpressionFlowState , isInStrictMode ?: boolean , parent ?: Node , subtreeFlags ?: number ) {
1557
+ workStacks . state [ stackIndex ] = state ;
1558
+ if ( isInStrictMode !== undefined ) {
1559
+ workStacks . inStrictMode [ stackIndex ] = isInStrictMode ;
1560
+ }
1561
+ if ( parent !== undefined ) {
1562
+ workStacks . parent [ stackIndex ] = parent ;
1563
+ }
1564
+ if ( subtreeFlags !== undefined ) {
1565
+ workStacks . subtreeFlags [ stackIndex ] = subtreeFlags ;
1458
1566
}
1459
1567
}
1460
- else {
1461
- bindEachChild ( node ) ;
1462
- if ( isAssignmentOperator ( operator ) && ! isAssignmentTarget ( node ) ) {
1463
- bindAssignmentTargetFlow ( node . left ) ;
1464
- if ( operator === SyntaxKind . EqualsToken && node . left . kind === SyntaxKind . ElementAccessExpression ) {
1465
- const elementAccess = < ElementAccessExpression > node . left ;
1466
- if ( isNarrowableOperand ( elementAccess . expression ) ) {
1467
- currentFlow = createFlowMutation ( FlowFlags . ArrayMutation , currentFlow , node ) ;
1468
- }
1568
+
1569
+ function completeNode ( ) {
1570
+ if ( workStacks . inStrictMode [ stackIndex ] !== undefined ) {
1571
+ if ( workStacks . subtreeFlags [ stackIndex ] === - 1 ) {
1572
+ skipTransformFlagAggregation = false ;
1573
+ subtreeTransformFlags |= node . transformFlags & ~ getTransformFlagsSubtreeExclusions ( node . kind ) ;
1574
+ }
1575
+ else if ( workStacks . subtreeFlags [ stackIndex ] !== undefined ) {
1576
+ subtreeTransformFlags = workStacks . subtreeFlags [ stackIndex ] ! | computeTransformFlagsForNode ( node , subtreeTransformFlags ) ;
1469
1577
}
1578
+ inStrictMode = workStacks . inStrictMode [ stackIndex ] ! ;
1579
+ parent = workStacks . parent [ stackIndex ] ! ;
1580
+ }
1581
+ stackIndex -- ;
1582
+ }
1583
+
1584
+ /**
1585
+ * If `node` is a BinaryExpression, adds it to the local work stack, otherwise recursively binds it
1586
+ */
1587
+ function maybeBind ( node : Node ) {
1588
+ if ( node && isBinaryExpression ( node ) ) {
1589
+ stackIndex ++ ;
1590
+ workStacks . expr [ stackIndex ] = node ;
1591
+ workStacks . state [ stackIndex ] = BindBinaryExpressionFlowState . BindThenBindChildren ;
1592
+ workStacks . inStrictMode [ stackIndex ] = undefined ;
1593
+ workStacks . parent [ stackIndex ] = undefined ;
1594
+ workStacks . subtreeFlags [ stackIndex ] = undefined ;
1595
+ }
1596
+ else {
1597
+ bind ( node ) ;
1470
1598
}
1471
1599
}
1472
1600
}
0 commit comments