@@ -227,6 +227,7 @@ namespace ts {
227
227
228
228
const unreachableFlow : FlowNode = { flags : FlowFlags . Unreachable } ;
229
229
const reportedUnreachableFlow : FlowNode = { flags : FlowFlags . Unreachable } ;
230
+ const bindBinaryExpressionFlow = createBindBinaryExpressionFlow ( ) ;
230
231
231
232
/**
232
233
* Inside the binder, we may create a diagnostic for an as-yet unbound node (with potentially no parent pointers, implying no accessible source file)
@@ -1497,132 +1498,110 @@ namespace ts {
1497
1498
bindAssignmentTargetFlow ( node . left ) ;
1498
1499
}
1499
1500
1500
- const enum BindBinaryExpressionFlowState {
1501
- BindThenBindChildren ,
1502
- MaybeBindLeft ,
1503
- BindToken ,
1504
- BindRight ,
1505
- FinishBind
1506
- }
1507
-
1508
- function bindBinaryExpressionFlow ( node : BinaryExpression ) {
1509
- const workStacks : {
1510
- expr : BinaryExpression [ ] ,
1511
- state : BindBinaryExpressionFlowState [ ] ,
1512
- inStrictMode : ( boolean | undefined ) [ ] ,
1513
- parent : ( Node | undefined ) [ ] ,
1514
- } = {
1515
- expr : [ node ] ,
1516
- state : [ BindBinaryExpressionFlowState . MaybeBindLeft ] ,
1517
- inStrictMode : [ undefined ] ,
1518
- parent : [ undefined ] ,
1519
- } ;
1520
- let stackIndex = 0 ;
1521
- while ( stackIndex >= 0 ) {
1522
- node = workStacks . expr [ stackIndex ] ;
1523
- switch ( workStacks . state [ stackIndex ] ) {
1524
- case BindBinaryExpressionFlowState . BindThenBindChildren : {
1525
- // This state is used only when recuring, to emulate the work that `bind` does before
1526
- // reaching `bindChildren`. A normal call to `bindBinaryExpressionFlow` will already have done this work.
1527
- setParent ( node , parent ) ;
1528
- const saveInStrictMode = inStrictMode ;
1529
- bindWorker ( node ) ;
1530
- const saveParent = parent ;
1531
- parent = node ;
1532
-
1533
- advanceState ( BindBinaryExpressionFlowState . MaybeBindLeft , saveInStrictMode , saveParent ) ;
1534
- break ;
1535
- }
1536
- case BindBinaryExpressionFlowState . MaybeBindLeft : {
1537
- const operator = node . operatorToken . kind ;
1538
- // TODO: bindLogicalExpression is recursive - if we want to handle deeply nested `&&` expressions
1539
- // we'll need to handle the `bindLogicalExpression` scenarios in this state machine, too
1540
- // For now, though, since the common cases are chained `+`, leaving it recursive is fine
1541
- if ( operator === SyntaxKind . AmpersandAmpersandToken || operator === SyntaxKind . BarBarToken || operator === SyntaxKind . QuestionQuestionToken ||
1542
- isLogicalOrCoalescingAssignmentOperator ( operator ) ) {
1543
- if ( isTopLevelLogicalExpression ( node ) ) {
1544
- const postExpressionLabel = createBranchLabel ( ) ;
1545
- bindLogicalLikeExpression ( node , postExpressionLabel , postExpressionLabel ) ;
1546
- currentFlow = finishFlowLabel ( postExpressionLabel ) ;
1547
- }
1548
- else {
1549
- bindLogicalLikeExpression ( node , currentTrueTarget ! , currentFalseTarget ! ) ;
1550
- }
1551
- completeNode ( ) ;
1552
- }
1553
- else {
1554
- advanceState ( BindBinaryExpressionFlowState . BindToken ) ;
1555
- maybeBind ( node . left ) ;
1556
- }
1557
- break ;
1558
- }
1559
- case BindBinaryExpressionFlowState . BindToken : {
1560
- if ( node . operatorToken . kind === SyntaxKind . CommaToken ) {
1561
- maybeBindExpressionFlowIfCall ( node . left ) ;
1562
- }
1563
- advanceState ( BindBinaryExpressionFlowState . BindRight ) ;
1564
- maybeBind ( node . operatorToken ) ;
1565
- break ;
1566
- }
1567
- case BindBinaryExpressionFlowState . BindRight : {
1568
- advanceState ( BindBinaryExpressionFlowState . FinishBind ) ;
1569
- maybeBind ( node . right ) ;
1570
- break ;
1501
+ function createBindBinaryExpressionFlow ( ) {
1502
+ interface WorkArea {
1503
+ stackIndex : number ;
1504
+ skip : boolean ;
1505
+ inStrictModeStack : ( boolean | undefined ) [ ] ;
1506
+ parentStack : ( Node | undefined ) [ ] ;
1507
+ }
1508
+
1509
+ return createBinaryExpressionTrampoline ( onEnter , onLeft , onOperator , onRight , onExit , /*foldState*/ undefined ) ;
1510
+
1511
+ function onEnter ( node : BinaryExpression , state : WorkArea | undefined ) {
1512
+ if ( state ) {
1513
+ state . stackIndex ++ ;
1514
+ // Emulate the work that `bind` does before reaching `bindChildren`. A normal call to
1515
+ // `bindBinaryExpressionFlow` will already have done this work.
1516
+ setParent ( node , parent ) ;
1517
+ const saveInStrictMode = inStrictMode ;
1518
+ bindWorker ( node ) ;
1519
+ const saveParent = parent ;
1520
+ parent = node ;
1521
+ state . skip = false ;
1522
+ state . inStrictModeStack [ state . stackIndex ] = saveInStrictMode ;
1523
+ state . parentStack [ state . stackIndex ] = saveParent ;
1524
+ }
1525
+ else {
1526
+ state = {
1527
+ stackIndex : 0 ,
1528
+ skip : false ,
1529
+ inStrictModeStack : [ undefined ] ,
1530
+ parentStack : [ undefined ]
1531
+ } ;
1532
+ }
1533
+ // TODO: bindLogicalExpression is recursive - if we want to handle deeply nested `&&` expressions
1534
+ // we'll need to handle the `bindLogicalExpression` scenarios in this state machine, too
1535
+ // For now, though, since the common cases are chained `+`, leaving it recursive is fine
1536
+ const operator = node . operatorToken . kind ;
1537
+ if ( operator === SyntaxKind . AmpersandAmpersandToken ||
1538
+ operator === SyntaxKind . BarBarToken ||
1539
+ operator === SyntaxKind . QuestionQuestionToken ||
1540
+ isLogicalOrCoalescingAssignmentOperator ( operator ) ) {
1541
+ if ( isTopLevelLogicalExpression ( node ) ) {
1542
+ const postExpressionLabel = createBranchLabel ( ) ;
1543
+ bindLogicalLikeExpression ( node , postExpressionLabel , postExpressionLabel ) ;
1544
+ currentFlow = finishFlowLabel ( postExpressionLabel ) ;
1571
1545
}
1572
- case BindBinaryExpressionFlowState . FinishBind : {
1573
- const operator = node . operatorToken . kind ;
1574
- if ( isAssignmentOperator ( operator ) && ! isAssignmentTarget ( node ) ) {
1575
- bindAssignmentTargetFlow ( node . left ) ;
1576
- if ( operator === SyntaxKind . EqualsToken && node . left . kind === SyntaxKind . ElementAccessExpression ) {
1577
- const elementAccess = < ElementAccessExpression > node . left ;
1578
- if ( isNarrowableOperand ( elementAccess . expression ) ) {
1579
- currentFlow = createFlowMutation ( FlowFlags . ArrayMutation , currentFlow , node ) ;
1580
- }
1581
- }
1582
- }
1583
- completeNode ( ) ;
1584
- break ;
1546
+ else {
1547
+ bindLogicalLikeExpression ( node , currentTrueTarget ! , currentFalseTarget ! ) ;
1585
1548
}
1586
- default : return Debug . fail ( `Invalid state ${ workStacks . state [ stackIndex ] } for bindBinaryExpressionFlow` ) ;
1549
+ state . skip = true ;
1587
1550
}
1551
+ return state ;
1588
1552
}
1589
1553
1590
- /**
1591
- * Note that `advanceState` sets the _current_ head state, and that `maybeBind` potentially pushes on a new
1592
- * head state; so `advanceState` must be called before any `maybeBind` during a state's execution.
1593
- */
1594
- function advanceState ( state : BindBinaryExpressionFlowState , isInStrictMode ?: boolean , parent ?: Node ) {
1595
- workStacks . state [ stackIndex ] = state ;
1596
- if ( isInStrictMode !== undefined ) {
1597
- workStacks . inStrictMode [ stackIndex ] = isInStrictMode ;
1554
+ function onLeft ( left : Expression , state : WorkArea , _node : BinaryExpression ) {
1555
+ if ( ! state . skip ) {
1556
+ return maybeBind ( left ) ;
1598
1557
}
1599
- if ( parent !== undefined ) {
1600
- workStacks . parent [ stackIndex ] = parent ;
1558
+ }
1559
+
1560
+ function onOperator ( operatorToken : BinaryOperatorToken , state : WorkArea , node : BinaryExpression ) {
1561
+ if ( ! state . skip ) {
1562
+ if ( operatorToken . kind === SyntaxKind . CommaToken ) {
1563
+ maybeBindExpressionFlowIfCall ( node . left ) ;
1564
+ }
1565
+ bind ( operatorToken ) ;
1601
1566
}
1602
1567
}
1603
1568
1604
- function completeNode ( ) {
1605
- if ( workStacks . inStrictMode [ stackIndex ] !== undefined ) {
1606
- inStrictMode = workStacks . inStrictMode [ stackIndex ] ! ;
1607
- parent = workStacks . parent [ stackIndex ] ! ;
1569
+ function onRight ( right : Expression , state : WorkArea , _node : BinaryExpression ) {
1570
+ if ( ! state . skip ) {
1571
+ return maybeBind ( right ) ;
1608
1572
}
1609
- stackIndex -- ;
1610
1573
}
1611
1574
1612
- /**
1613
- * If `node` is a BinaryExpression, adds it to the local work stack, otherwise recursively binds it
1614
- */
1575
+ function onExit ( node : BinaryExpression , state : WorkArea ) {
1576
+ if ( ! state . skip ) {
1577
+ const operator = node . operatorToken . kind ;
1578
+ if ( isAssignmentOperator ( operator ) && ! isAssignmentTarget ( node ) ) {
1579
+ bindAssignmentTargetFlow ( node . left ) ;
1580
+ if ( operator === SyntaxKind . EqualsToken && node . left . kind === SyntaxKind . ElementAccessExpression ) {
1581
+ const elementAccess = < ElementAccessExpression > node . left ;
1582
+ if ( isNarrowableOperand ( elementAccess . expression ) ) {
1583
+ currentFlow = createFlowMutation ( FlowFlags . ArrayMutation , currentFlow , node ) ;
1584
+ }
1585
+ }
1586
+ }
1587
+ }
1588
+ const savedInStrictMode = state . inStrictModeStack [ state . stackIndex ] ;
1589
+ const savedParent = state . parentStack [ state . stackIndex ] ;
1590
+ if ( savedInStrictMode !== undefined ) {
1591
+ inStrictMode = savedInStrictMode ;
1592
+ }
1593
+ if ( savedParent !== undefined ) {
1594
+ parent = savedParent ;
1595
+ }
1596
+ state . skip = false ;
1597
+ state . stackIndex -- ;
1598
+ }
1599
+
1615
1600
function maybeBind ( node : Node ) {
1616
1601
if ( node && isBinaryExpression ( node ) && ! isDestructuringAssignment ( node ) ) {
1617
- stackIndex ++ ;
1618
- workStacks . expr [ stackIndex ] = node ;
1619
- workStacks . state [ stackIndex ] = BindBinaryExpressionFlowState . BindThenBindChildren ;
1620
- workStacks . inStrictMode [ stackIndex ] = undefined ;
1621
- workStacks . parent [ stackIndex ] = undefined ;
1622
- }
1623
- else {
1624
- bind ( node ) ;
1602
+ return node ;
1625
1603
}
1604
+ bind ( node ) ;
1626
1605
}
1627
1606
}
1628
1607
0 commit comments